Merge "MCE: Set message read status or deleted status"
diff --git a/.style.yapf b/.style.yapf
new file mode 100644
index 0000000..2b11922
--- /dev/null
+++ b/.style.yapf
@@ -0,0 +1,5 @@
+[style]
+# http://google.github.io/styleguide/pyguide.html
+based_on_style: google
+indent_width: 4
+column_limit: 120
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/OWNERS b/OWNERS
index aeb2b83..ab11e8c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,5 +7,6 @@
 jpawlowski@google.com
 mylesgw@google.com
 optedoblivion@google.com
+rahulsabnis@google.com
 siyuanh@google.com
 stng@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 380216c..a473040 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,4 +6,5 @@
 
 [Hook Scripts]
 aosp_first = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} ".*$"
+yapf_hook = ./tools/scripts/yapf_checker.py
 
diff --git a/README.md b/README.md
index 869bc1f..19e33d7 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,7 @@
 ### Eclipse IDE Support
 
 1. Follows the Chromium project
- [Eclipse Setup Instructions](https://chromium.googlesource.com/chromium/src/+/master/docs/linux_eclipse_dev.md)
+ [Eclipse Setup Instructions](https://chromium.googlesource.com/chromium/src.git/+/master/docs/linux/eclipse_dev.md)
  until "Optional: Building inside Eclipse" section (don't do that section, we
  will set it up differently)
 
diff --git a/TEST_MAPPING b/TEST_MAPPING
old mode 100644
new mode 100755
index 5f6e378..cc24d8b
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -76,6 +76,24 @@
     {
       "name" : "net_test_types",
       "host" : true
+    },
+    {
+      "name" : "net_test_btif_rc",
+      "host" : true
+    },
+    {
+      "name" : "net_test_stack_a2dp_native",
+      "host" : true
+    },
+    {
+      "name" : "net_test_btif_config_cache",
+      "host" : true
+    },
+    {
+      "name" : "net_test_hf_client_add_record"
+    },
+    {
+      "name" : "net_test_btif_hf_client_service"
     }
   ]
 }
diff --git a/audio_a2dp_hw/src/audio_a2dp_hw.cc b/audio_a2dp_hw/src/audio_a2dp_hw.cc
index 2fb139f..43f3a1b 100644
--- a/audio_a2dp_hw/src/audio_a2dp_hw.cc
+++ b/audio_a2dp_hw/src/audio_a2dp_hw.cc
@@ -70,12 +70,11 @@
 // sockets
 #define WRITE_POLL_MS 20
 
-#define FNLOG() LOG_VERBOSE(LOG_TAG, "%s", __func__);
-#define DEBUG(fmt, ...) \
-  LOG_VERBOSE(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
-#define INFO(fmt, ...) LOG_INFO(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
-#define WARN(fmt, ...) LOG_WARN(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
-#define ERROR(fmt, ...) LOG_ERROR(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
+#define FNLOG() LOG_VERBOSE("%s", __func__);
+#define DEBUG(fmt, ...) LOG_VERBOSE("%s: " fmt, __func__, ##__VA_ARGS__)
+#define INFO(fmt, ...) LOG_INFO("%s: " fmt, __func__, ##__VA_ARGS__)
+#define WARN(fmt, ...) LOG_WARN("%s: " fmt, __func__, ##__VA_ARGS__)
+#define ERROR(fmt, ...) LOG_ERROR("%s: " fmt, __func__, ##__VA_ARGS__)
 
 #define ASSERTC(cond, msg, val)                                           \
   if (!(cond)) {                                                          \
diff --git a/audio_bluetooth_hw/audio_bluetooth_hw.cc b/audio_bluetooth_hw/audio_bluetooth_hw.cc
index e32d88b..887c4e3 100644
--- a/audio_bluetooth_hw/audio_bluetooth_hw.cc
+++ b/audio_bluetooth_hw/audio_bluetooth_hw.cc
@@ -18,7 +18,6 @@
 
 #include <android-base/logging.h>
 #include <errno.h>
-#include <hardware/audio.h>
 #include <hardware/hardware.h>
 #include <log/log.h>
 #include <malloc.h>
@@ -28,10 +27,31 @@
 #include "stream_apis.h"
 #include "utils.h"
 
+using ::android::bluetooth::audio::utils::GetAudioParamString;
+using ::android::bluetooth::audio::utils::ParseAudioParams;
+
 static int adev_set_parameters(struct audio_hw_device* dev,
                                const char* kvpairs) {
   LOG(VERBOSE) << __func__ << ": kevpairs=[" << kvpairs << "]";
-  return -ENOSYS;
+  std::unordered_map<std::string, std::string> params =
+      ParseAudioParams(kvpairs);
+  if (params.empty()) return 0;
+
+  LOG(VERBOSE) << __func__ << ": ParamsMap=[" << GetAudioParamString(params)
+               << "]";
+  if (params.find("A2dpSuspended") == params.end()) {
+    return -ENOSYS;
+  }
+
+  auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
+  std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
+  for (auto sout : bluetooth_device->opened_stream_outs_) {
+    if (sout->stream_out_.common.set_parameters != nullptr) {
+      sout->stream_out_.common.set_parameters(&sout->stream_out_.common,
+                                              kvpairs);
+    }
+  }
+  return 0;
 }
 
 static char* adev_get_parameters(const struct audio_hw_device* dev,
@@ -82,7 +102,8 @@
 static int adev_dump(const audio_hw_device_t* device, int fd) { return 0; }
 
 static int adev_close(hw_device_t* device) {
-  free(device);
+  auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(device);
+  delete bluetooth_device;
   return 0;
 }
 
@@ -91,8 +112,8 @@
   LOG(VERBOSE) << __func__ << ": name=[" << name << "]";
   if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
 
-  struct audio_hw_device* adev =
-      (struct audio_hw_device*)calloc(1, sizeof(struct audio_hw_device));
+  auto bluetooth_audio_device = new BluetoothAudioDevice{};
+  struct audio_hw_device* adev = &bluetooth_audio_device->audio_device_;
   if (!adev) return -ENOMEM;
 
   adev->common.tag = HARDWARE_DEVICE_TAG;
diff --git a/audio_bluetooth_hw/device_port_proxy.cc b/audio_bluetooth_hw/device_port_proxy.cc
index 6e1fda2..d3a9e82 100644
--- a/audio_bluetooth_hw/device_port_proxy.cc
+++ b/audio_bluetooth_hw/device_port_proxy.cc
@@ -38,7 +38,6 @@
 using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
 using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
 using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
-using ::android::hardware::bluetooth::audio::V2_0::SessionType;
 using BluetoothAudioStatus =
     ::android::hardware::bluetooth::audio::V2_0::Status;
 using ControlResultCallback = std::function<void(
diff --git a/audio_bluetooth_hw/device_port_proxy.h b/audio_bluetooth_hw/device_port_proxy.h
index 16db274..160df4b 100644
--- a/audio_bluetooth_hw/device_port_proxy.h
+++ b/audio_bluetooth_hw/device_port_proxy.h
@@ -28,6 +28,8 @@
 namespace bluetooth {
 namespace audio {
 
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
 // Proxy for Bluetooth Audio HW Module to communicate with Bluetooth Audio
 // Session Control. All methods are not thread safe, so users must acquire a
 // lock. Note: currently, in stream_apis.cc, if GetState() is only used for
@@ -84,9 +86,14 @@
   // Set the current BluetoothStreamState
   void SetState(BluetoothStreamState state);
 
+  bool IsA2dp() const {
+    return session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+           session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
+  }
+
  private:
   BluetoothStreamState state_;
-  ::android::hardware::bluetooth::audio::V2_0::SessionType session_type_;
+  SessionType session_type_;
   uint16_t cookie_;
   mutable std::mutex cv_mutex_;
   std::condition_variable internal_cv_;
diff --git a/audio_bluetooth_hw/stream_apis.cc b/audio_bluetooth_hw/stream_apis.cc
index 9b4c5cf..d809b57 100644
--- a/audio_bluetooth_hw/stream_apis.cc
+++ b/audio_bluetooth_hw/stream_apis.cc
@@ -289,7 +289,8 @@
               << routing_param->second.c_str() << "'";
   }
 
-  if (params.find("A2dpSuspended") != params.end()) {
+  if (params.find("A2dpSuspended") != params.end() &&
+      out->bluetooth_output_.IsA2dp()) {
     if (params["A2dpSuspended"] == "true") {
       LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_.GetState()
                 << " stream param stopped";
@@ -634,7 +635,7 @@
                             struct audio_stream_out** stream_out,
                             const char* address __unused) {
   *stream_out = nullptr;
-  auto* out = new BluetoothStreamOut;
+  auto* out = new BluetoothStreamOut{};
   if (!out->bluetooth_output_.SetUp(devices)) {
     delete out;
     return -EINVAL;
@@ -683,6 +684,11 @@
   out->frames_rendered_ = 0;
   out->frames_presented_ = 0;
 
+  {
+    auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
+    std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
+    bluetooth_device->opened_stream_outs_.push_back(out);
+  }
   *stream_out = &out->stream_out_;
   LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_.GetState() << ", sample_rate=" << out->sample_rate_
             << ", channels=" << StringPrintf("%#x", out->channel_mask_) << ", format=" << out->format_
@@ -695,6 +701,11 @@
   auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
   LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_.GetState()
                << ", stopping";
+  {
+    auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
+    std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
+    bluetooth_device->opened_stream_outs_.remove(out);
+  }
   if (out->bluetooth_output_.GetState() != BluetoothStreamState::DISABLED) {
     out->frames_rendered_ = 0;
     out->frames_presented_ = 0;
diff --git a/audio_bluetooth_hw/stream_apis.h b/audio_bluetooth_hw/stream_apis.h
index 14a955c..c894d1e 100644
--- a/audio_bluetooth_hw/stream_apis.h
+++ b/audio_bluetooth_hw/stream_apis.h
@@ -18,6 +18,7 @@
 
 #include <hardware/audio.h>
 #include <system/audio.h>
+#include <list>
 
 #include "device_port_proxy.h"
 
@@ -45,7 +46,7 @@
 struct BluetoothStreamOut {
   // Must be the first member so it can be cast from audio_stream
   // or audio_stream_out pointer
-  audio_stream_out stream_out_;
+  audio_stream_out stream_out_{};
   ::android::bluetooth::audio::BluetoothAudioPortOut bluetooth_output_;
   int64_t last_write_time_us_;
   // Audio PCM Configs
@@ -62,6 +63,16 @@
   mutable std::mutex mutex_;
 };
 
+struct BluetoothAudioDevice {
+  // Important: device must be first as an audio_hw_device* may be cast to
+  // BluetoothAudioDevice* when the type is implicitly known.
+  audio_hw_device audio_device_{};
+  // protect against device->output and stream_out from being inconsistent
+  std::mutex mutex_;
+  std::list<BluetoothStreamOut*> opened_stream_outs_ =
+      std::list<BluetoothStreamOut*>(0);
+};
+
 int adev_open_output_stream(struct audio_hw_device* dev,
                             audio_io_handle_t handle, audio_devices_t devices,
                             audio_output_flags_t flags,
diff --git a/audio_hal_interface/Android.bp b/audio_hal_interface/Android.bp
index 94a77ff..92cf1b1 100644
--- a/audio_hal_interface/Android.bp
+++ b/audio_hal_interface/Android.bp
@@ -13,6 +13,7 @@
     srcs: [
         "a2dp_encoding.cc",
         "client_interface.cc",
+        "codec_status.cc",
         "hearing_aid_software_encoding.cc",
     ],
     shared_libs: [
@@ -36,6 +37,7 @@
     defaults: ["fluoride_defaults"],
     include_dirs: [
         "system/bt",
+        "system/bt/stack/include",
     ],
     srcs: [
         "client_interface_unittest.cc",
diff --git a/audio_hal_interface/a2dp_encoding.cc b/audio_hal_interface/a2dp_encoding.cc
index 1b362e5..456ba60 100644
--- a/audio_hal_interface/a2dp_encoding.cc
+++ b/audio_hal_interface/a2dp_encoding.cc
@@ -16,11 +16,8 @@
 
 #include "a2dp_encoding.h"
 #include "client_interface.h"
+#include "codec_status.h"
 
-#include "a2dp_aac_constants.h"
-#include "a2dp_sbc_constants.h"
-#include "a2dp_vendor_ldac_constants.h"
-#include "bta/av/bta_av_int.h"
 #include "btif_a2dp_source.h"
 #include "btif_av.h"
 #include "btif_av_co.h"
@@ -29,30 +26,24 @@
 
 namespace {
 
-using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
-using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
-using ::android::hardware::bluetooth::audio::V2_0::CodecType;
-using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
-using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
-using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
-using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
-using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
-using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::bluetooth::audio::AudioCapabilities;
 using ::bluetooth::audio::AudioConfiguration;
 using ::bluetooth::audio::BitsPerSample;
 using ::bluetooth::audio::BluetoothAudioCtrlAck;
 using ::bluetooth::audio::ChannelMode;
-using ::bluetooth::audio::CodecConfiguration;
 using ::bluetooth::audio::PcmParameters;
 using ::bluetooth::audio::SampleRate;
 using ::bluetooth::audio::SessionType;
 
-const CodecConfiguration kInvalidCodecConfiguration = {
-    .codecType = CodecType::UNKNOWN,
-    .encodedAudioBitrate = 0x00000000,
-    .peerMtu = 0xffff,
-    .isScmstEnabled = false,
-    .config = {}};
+using ::bluetooth::audio::BluetoothAudioClientInterface;
+using ::bluetooth::audio::codec::A2dpAacToHalConfig;
+using ::bluetooth::audio::codec::A2dpAptxToHalConfig;
+using ::bluetooth::audio::codec::A2dpCodecToHalBitsPerSample;
+using ::bluetooth::audio::codec::A2dpCodecToHalChannelMode;
+using ::bluetooth::audio::codec::A2dpCodecToHalSampleRate;
+using ::bluetooth::audio::codec::A2dpLdacToHalConfig;
+using ::bluetooth::audio::codec::A2dpSbcToHalConfig;
+using ::bluetooth::audio::codec::CodecConfiguration;
 
 BluetoothAudioCtrlAck a2dp_ack_to_bt_audio_ctrl_ack(tA2DP_CTRL_ACK ack);
 
@@ -61,10 +52,11 @@
  public:
   A2dpTransport(SessionType sessionType)
       : IBluetoothTransportInstance(sessionType, {}),
-        a2dp_pending_cmd_(A2DP_CTRL_CMD_NONE),
-        remote_delay_report_(0),
         total_bytes_read_(0),
-        data_position_({}){};
+        data_position_({}) {
+    a2dp_pending_cmd_ = A2DP_CTRL_CMD_NONE;
+    remote_delay_report_ = 0;
+  }
 
   BluetoothAudioCtrlAck StartRequest() override {
     // Check if a previous request is not finished
@@ -188,19 +180,21 @@
   }
 
  private:
-  tA2DP_CTRL_CMD a2dp_pending_cmd_;
-  uint16_t remote_delay_report_;
+  static tA2DP_CTRL_CMD a2dp_pending_cmd_;
+  static uint16_t remote_delay_report_;
   uint64_t total_bytes_read_;
   timespec data_position_;
 };
 
-A2dpTransport* a2dp_sink = nullptr;
+tA2DP_CTRL_CMD A2dpTransport::a2dp_pending_cmd_ = A2DP_CTRL_CMD_NONE;
+uint16_t A2dpTransport::remote_delay_report_ = 0;
 
 // Common interface to call-out into Bluetooth Audio HAL
-bluetooth::audio::BluetoothAudioClientInterface* a2dp_hal_clientif = nullptr;
-auto session_type = SessionType::UNKNOWN;
+BluetoothAudioClientInterface* software_hal_interface = nullptr;
+BluetoothAudioClientInterface* offloading_hal_interface = nullptr;
+BluetoothAudioClientInterface* active_hal_interface = nullptr;
 
-// Save the value if the remote reports its delay before a2dp_sink is
+// Save the value if the remote reports its delay before this interface is
 // initialized
 uint16_t remote_delay = 0;
 
@@ -226,301 +220,43 @@
   }
 }
 
-SampleRate a2dp_codec_to_hal_sample_rate(
-    const btav_a2dp_codec_config_t& a2dp_codec_config) {
-  switch (a2dp_codec_config.sample_rate) {
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
-      return SampleRate::RATE_44100;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
-      return SampleRate::RATE_48000;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
-      return SampleRate::RATE_88200;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
-      return SampleRate::RATE_96000;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
-      return SampleRate::RATE_176400;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
-      return SampleRate::RATE_192000;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_16000:
-      return SampleRate::RATE_16000;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_24000:
-      return SampleRate::RATE_24000;
-    default:
-      return SampleRate::RATE_UNKNOWN;
-  }
-}
-
-BitsPerSample a2dp_codec_to_hal_bits_per_sample(
-    const btav_a2dp_codec_config_t& a2dp_codec_config) {
-  switch (a2dp_codec_config.bits_per_sample) {
-    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
-      return BitsPerSample::BITS_16;
-    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
-      return BitsPerSample::BITS_24;
-    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
-      return BitsPerSample::BITS_32;
-    default:
-      return BitsPerSample::BITS_UNKNOWN;
-  }
-}
-
-ChannelMode a2dp_codec_to_hal_channel_mode(
-    const btav_a2dp_codec_config_t& a2dp_codec_config) {
-  switch (a2dp_codec_config.channel_mode) {
-    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
-      return ChannelMode::MONO;
-    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
-      return ChannelMode::STEREO;
-    default:
-      return ChannelMode::UNKNOWN;
-  }
-}
-
 bool a2dp_get_selected_hal_codec_config(CodecConfiguration* codec_config) {
-  A2dpCodecConfig* a2dp_codec_configs = bta_av_get_a2dp_current_codec();
-  if (a2dp_codec_configs == nullptr) {
+  A2dpCodecConfig* a2dp_config = bta_av_get_a2dp_current_codec();
+  if (a2dp_config == nullptr) {
     LOG(WARNING) << __func__ << ": failure to get A2DP codec config";
-    *codec_config = kInvalidCodecConfiguration;
+    *codec_config = ::bluetooth::audio::codec::kInvalidCodecConfiguration;
     return false;
   }
-  btav_a2dp_codec_config_t current_codec = a2dp_codec_configs->getCodecConfig();
-  tBT_A2DP_OFFLOAD a2dp_offload;
-  a2dp_codec_configs->getCodecSpecificConfig(&a2dp_offload);
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
   switch (current_codec.codec_type) {
     case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
       [[fallthrough]];
     case BTAV_A2DP_CODEC_INDEX_SINK_SBC: {
-      codec_config->codecType = CodecType::SBC;
-      codec_config->config.sbcConfig({});
-      auto sbc_config = codec_config->config.sbcConfig();
-      sbc_config.sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-      if (sbc_config.sampleRate == SampleRate::RATE_UNKNOWN) {
-        LOG(ERROR) << __func__
-                   << ": Unknown SBC sample_rate=" << current_codec.sample_rate;
+      if (!A2dpSbcToHalConfig(codec_config, a2dp_config)) {
         return false;
       }
-      uint8_t channel_mode = a2dp_offload.codec_info[3] & A2DP_SBC_IE_CH_MD_MSK;
-      switch (channel_mode) {
-        case A2DP_SBC_IE_CH_MD_JOINT:
-          sbc_config.channelMode = SbcChannelMode::JOINT_STEREO;
-          break;
-        case A2DP_SBC_IE_CH_MD_STEREO:
-          sbc_config.channelMode = SbcChannelMode::STEREO;
-          break;
-        case A2DP_SBC_IE_CH_MD_DUAL:
-          sbc_config.channelMode = SbcChannelMode::DUAL;
-          break;
-        case A2DP_SBC_IE_CH_MD_MONO:
-          sbc_config.channelMode = SbcChannelMode::MONO;
-          break;
-        default:
-          LOG(ERROR) << __func__
-                     << ": Unknown SBC channel_mode=" << channel_mode;
-          sbc_config.channelMode = SbcChannelMode::UNKNOWN;
-          return false;
-      }
-      uint8_t block_length =
-          a2dp_offload.codec_info[0] & A2DP_SBC_IE_BLOCKS_MSK;
-      switch (block_length) {
-        case A2DP_SBC_IE_BLOCKS_4:
-          sbc_config.blockLength = SbcBlockLength::BLOCKS_4;
-          break;
-        case A2DP_SBC_IE_BLOCKS_8:
-          sbc_config.blockLength = SbcBlockLength::BLOCKS_8;
-          break;
-        case A2DP_SBC_IE_BLOCKS_12:
-          sbc_config.blockLength = SbcBlockLength::BLOCKS_12;
-          break;
-        case A2DP_SBC_IE_BLOCKS_16:
-          sbc_config.blockLength = SbcBlockLength::BLOCKS_16;
-          break;
-        default:
-          LOG(ERROR) << __func__
-                     << ": Unknown SBC block_length=" << block_length;
-          return false;
-      }
-      uint8_t sub_bands = a2dp_offload.codec_info[0] & A2DP_SBC_IE_SUBBAND_MSK;
-      switch (sub_bands) {
-        case A2DP_SBC_IE_SUBBAND_4:
-          sbc_config.numSubbands = SbcNumSubbands::SUBBAND_4;
-          break;
-        case A2DP_SBC_IE_SUBBAND_8:
-          sbc_config.numSubbands = SbcNumSubbands::SUBBAND_8;
-          break;
-        default:
-          LOG(ERROR) << __func__ << ": Unknown SBC Subbands=" << sub_bands;
-          return false;
-      }
-      uint8_t alloc_method =
-          a2dp_offload.codec_info[0] & A2DP_SBC_IE_ALLOC_MD_MSK;
-      switch (alloc_method) {
-        case A2DP_SBC_IE_ALLOC_MD_S:
-          sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_S;
-          break;
-        case A2DP_SBC_IE_ALLOC_MD_L:
-          sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_L;
-          break;
-        default:
-          LOG(ERROR) << __func__
-                     << ": Unknown SBC alloc_method=" << alloc_method;
-          return false;
-      }
-      sbc_config.minBitpool = a2dp_offload.codec_info[1];
-      sbc_config.maxBitpool = a2dp_offload.codec_info[2];
-      sbc_config.bitsPerSample =
-          a2dp_codec_to_hal_bits_per_sample(current_codec);
-      if (sbc_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown SBC bits_per_sample="
-                   << current_codec.bits_per_sample;
-        return false;
-      }
-      codec_config->config.sbcConfig(sbc_config);
       break;
     }
     case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
       [[fallthrough]];
     case BTAV_A2DP_CODEC_INDEX_SINK_AAC: {
-      codec_config->codecType = CodecType::AAC;
-      codec_config->config.aacConfig({});
-      auto aac_config = codec_config->config.aacConfig();
-      uint8_t object_type = a2dp_offload.codec_info[0];
-      switch (object_type) {
-        case A2DP_AAC_OBJECT_TYPE_MPEG2_LC:
-          aac_config.objectType = AacObjectType::MPEG2_LC;
-          break;
-        case A2DP_AAC_OBJECT_TYPE_MPEG4_LC:
-          aac_config.objectType = AacObjectType::MPEG4_LC;
-          break;
-        case A2DP_AAC_OBJECT_TYPE_MPEG4_LTP:
-          aac_config.objectType = AacObjectType::MPEG4_LTP;
-          break;
-        case A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE:
-          aac_config.objectType = AacObjectType::MPEG4_SCALABLE;
-          break;
-        default:
-          LOG(ERROR) << __func__
-                     << ": Unknown AAC object_type=" << +object_type;
-          return false;
-      }
-      aac_config.sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-      if (aac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
-        LOG(ERROR) << __func__
-                   << ": Unknown AAC sample_rate=" << current_codec.sample_rate;
+      if (!A2dpAacToHalConfig(codec_config, a2dp_config)) {
         return false;
       }
-      aac_config.channelMode = a2dp_codec_to_hal_channel_mode(current_codec);
-      if (aac_config.channelMode == ChannelMode::UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown AAC channel_mode="
-                   << current_codec.channel_mode;
-        return false;
-      }
-      uint8_t vbr_enabled =
-          a2dp_offload.codec_info[1] & A2DP_AAC_VARIABLE_BIT_RATE_MASK;
-      switch (vbr_enabled) {
-        case A2DP_AAC_VARIABLE_BIT_RATE_ENABLED:
-          aac_config.variableBitRateEnabled = AacVariableBitRate::ENABLED;
-          break;
-        case A2DP_AAC_VARIABLE_BIT_RATE_DISABLED:
-          aac_config.variableBitRateEnabled = AacVariableBitRate::DISABLED;
-          break;
-        default:
-          LOG(ERROR) << __func__ << ": Unknown AAC VBR=" << +vbr_enabled;
-          return false;
-      }
-      aac_config.bitsPerSample =
-          a2dp_codec_to_hal_bits_per_sample(current_codec);
-      if (aac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown AAC bits_per_sample="
-                   << current_codec.bits_per_sample;
-        return false;
-      }
-      codec_config->config.aacConfig(aac_config);
       break;
     }
     case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
       [[fallthrough]];
     case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: {
-      if (current_codec.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX) {
-        codec_config->codecType = CodecType::APTX;
-      } else {
-        codec_config->codecType = CodecType::APTX_HD;
-      }
-      codec_config->config.aptxConfig({});
-      auto aptx_config = codec_config->config.aptxConfig();
-      aptx_config.sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-      if (aptx_config.sampleRate == SampleRate::RATE_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown aptX sample_rate="
-                   << current_codec.sample_rate;
+      if (!A2dpAptxToHalConfig(codec_config, a2dp_config)) {
         return false;
       }
-      aptx_config.channelMode = a2dp_codec_to_hal_channel_mode(current_codec);
-      if (aptx_config.channelMode == ChannelMode::UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown aptX channel_mode="
-                   << current_codec.channel_mode;
-        return false;
-      }
-      aptx_config.bitsPerSample =
-          a2dp_codec_to_hal_bits_per_sample(current_codec);
-      if (aptx_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown aptX bits_per_sample="
-                   << current_codec.bits_per_sample;
-        return false;
-      }
-      codec_config->config.aptxConfig(aptx_config);
       break;
     }
     case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: {
-      codec_config->codecType = CodecType::LDAC;
-      codec_config->config.ldacConfig({});
-      auto ldac_config = codec_config->config.ldacConfig();
-      ldac_config.sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-      if (ldac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown LDAC sample_rate="
-                   << current_codec.sample_rate;
+      if (!A2dpLdacToHalConfig(codec_config, a2dp_config)) {
         return false;
       }
-      switch (a2dp_offload.codec_info[7]) {
-        case A2DP_LDAC_CHANNEL_MODE_STEREO:
-          ldac_config.channelMode = LdacChannelMode::STEREO;
-          break;
-        case A2DP_LDAC_CHANNEL_MODE_DUAL:
-          ldac_config.channelMode = LdacChannelMode::DUAL;
-          break;
-        case A2DP_LDAC_CHANNEL_MODE_MONO:
-          ldac_config.channelMode = LdacChannelMode::MONO;
-          break;
-        default:
-          LOG(ERROR) << __func__ << ": Unknown LDAC channel_mode="
-                     << a2dp_offload.codec_info[7];
-          ldac_config.channelMode = LdacChannelMode::UNKNOWN;
-          return false;
-      }
-      switch (a2dp_offload.codec_info[6]) {
-        case A2DP_LDAC_QUALITY_HIGH:
-          ldac_config.qualityIndex = LdacQualityIndex::QUALITY_HIGH;
-          break;
-        case A2DP_LDAC_QUALITY_MID:
-          ldac_config.qualityIndex = LdacQualityIndex::QUALITY_MID;
-          break;
-        case A2DP_LDAC_QUALITY_LOW:
-          ldac_config.qualityIndex = LdacQualityIndex::QUALITY_LOW;
-          break;
-        case A2DP_LDAC_QUALITY_ABR_OFFLOAD:
-          ldac_config.qualityIndex = LdacQualityIndex::QUALITY_ABR;
-          break;
-        default:
-          LOG(ERROR) << __func__ << ": Unknown LDAC QualityIndex="
-                     << a2dp_offload.codec_info[6];
-          return false;
-      }
-      ldac_config.bitsPerSample =
-          a2dp_codec_to_hal_bits_per_sample(current_codec);
-      if (ldac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown LDAC bits_per_sample="
-                   << current_codec.bits_per_sample;
-        return false;
-      }
-      codec_config->config.ldacConfig(ldac_config);
       break;
     }
     case BTAV_A2DP_CODEC_INDEX_MAX:
@@ -528,16 +264,15 @@
     default:
       LOG(ERROR) << __func__
                  << ": Unknown codec_type=" << current_codec.codec_type;
-      codec_config->codecType = CodecType::UNKNOWN;
-      codec_config->config = {};
+      *codec_config = ::bluetooth::audio::codec::kInvalidCodecConfiguration;
       return false;
   }
-  codec_config->encodedAudioBitrate = a2dp_codec_configs->getTrackBitRate();
+  codec_config->encodedAudioBitrate = a2dp_config->getTrackBitRate();
   // Obtain the MTU
   RawAddress peer_addr = btif_av_source_active_peer();
   tA2DP_ENCODER_INIT_PEER_PARAMS peer_param;
   bta_av_co_get_peer_params(peer_addr, &peer_param);
-  int effectiveMtu = a2dp_codec_configs->getEffectiveMtu();
+  int effectiveMtu = a2dp_config->getEffectiveMtu();
   if (effectiveMtu > 0 && effectiveMtu < peer_param.peer_mtu) {
     codec_config->peerMtu = effectiveMtu;
   } else {
@@ -558,9 +293,9 @@
   }
 
   btav_a2dp_codec_config_t current_codec = a2dp_codec_configs->getCodecConfig();
-  pcm_config->sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-  pcm_config->bitsPerSample = a2dp_codec_to_hal_bits_per_sample(current_codec);
-  pcm_config->channelMode = a2dp_codec_to_hal_channel_mode(current_codec);
+  pcm_config->sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  pcm_config->bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  pcm_config->channelMode = A2dpCodecToHalChannelMode(current_codec);
   return (pcm_config->sampleRate != SampleRate::RATE_UNKNOWN &&
           pcm_config->bitsPerSample != BitsPerSample::BITS_UNKNOWN &&
           pcm_config->channelMode != ChannelMode::UNKNOWN);
@@ -574,15 +309,29 @@
   }
   return btaudio_a2dp_disabled;
 }
-
 }  // namespace
 
 namespace bluetooth {
 namespace audio {
 namespace a2dp {
 
+bool update_codec_offloading_capabilities(
+    const std::vector<btav_a2dp_codec_config_t>& framework_preference) {
+  return ::bluetooth::audio::codec::UpdateOffloadingCapabilities(
+      framework_preference);
+}
+
 // Checking if new bluetooth_audio is enabled
-bool is_hal_2_0_enabled() { return a2dp_hal_clientif != nullptr; }
+bool is_hal_2_0_enabled() { return active_hal_interface != nullptr; }
+
+// Check if new bluetooth_audio is running with offloading encoders
+bool is_hal_2_0_offloading() {
+  if (!is_hal_2_0_enabled()) {
+    return false;
+  }
+  return active_hal_interface->GetTransportInstance()->GetSessionType() ==
+         SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
+}
 
 // Initialize BluetoothAudio HAL: openProvider
 bool init(bluetooth::common::MessageLoopThread* message_loop) {
@@ -593,27 +342,47 @@
     return false;
   }
 
-  if (btif_av_is_a2dp_offload_enabled()) {
-    session_type = SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
-  } else {
-    session_type = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
-  }
-  a2dp_sink = new A2dpTransport(session_type);
-  a2dp_hal_clientif = new bluetooth::audio::BluetoothAudioClientInterface(
+  auto a2dp_sink =
+      new A2dpTransport(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+  software_hal_interface = new bluetooth::audio::BluetoothAudioClientInterface(
       a2dp_sink, message_loop);
-  if (!a2dp_hal_clientif->IsValid()) {
-    LOG(WARNING) << __func__ << ": BluetoothAudio HAL for A2DP session=" << toString(session_type) << " is invalid?!";
-    delete a2dp_hal_clientif;
-    a2dp_hal_clientif = nullptr;
+  if (!software_hal_interface->IsValid()) {
+    LOG(WARNING) << __func__ << ": BluetoothAudio HAL for A2DP is invalid?!";
+    delete software_hal_interface;
+    software_hal_interface = nullptr;
     delete a2dp_sink;
-    a2dp_sink = nullptr;
     return false;
   }
 
+  if (btif_av_is_a2dp_offload_enabled()) {
+    a2dp_sink = new A2dpTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+    offloading_hal_interface =
+        new bluetooth::audio::BluetoothAudioClientInterface(a2dp_sink,
+                                                            message_loop);
+    if (!offloading_hal_interface->IsValid()) {
+      LOG(FATAL) << __func__
+                 << ": BluetoothAudio HAL for A2DP offloading is invalid?!";
+      delete offloading_hal_interface;
+      offloading_hal_interface = nullptr;
+      delete a2dp_sink;
+      a2dp_sink = static_cast<A2dpTransport*>(
+          software_hal_interface->GetTransportInstance());
+      delete software_hal_interface;
+      software_hal_interface = nullptr;
+      delete a2dp_sink;
+      return false;
+    }
+  }
+
+  active_hal_interface =
+      (offloading_hal_interface != nullptr ? offloading_hal_interface
+                                           : software_hal_interface);
+
   if (remote_delay != 0) {
     LOG(INFO) << __func__ << ": restore DELAY "
               << static_cast<float>(remote_delay / 10.0) << " ms";
-    a2dp_sink->SetRemoteDelay(remote_delay);
+    static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance())
+        ->SetRemoteDelay(remote_delay);
     remote_delay = 0;
   }
   return true;
@@ -623,11 +392,23 @@
 void cleanup() {
   if (!is_hal_2_0_enabled()) return;
   end_session();
-  delete a2dp_hal_clientif;
-  a2dp_hal_clientif = nullptr;
+
+  auto a2dp_sink = active_hal_interface->GetTransportInstance();
+  static_cast<A2dpTransport*>(a2dp_sink)->ResetPendingCmd();
+  static_cast<A2dpTransport*>(a2dp_sink)->ResetPresentationPosition();
+  active_hal_interface = nullptr;
+
+  a2dp_sink = software_hal_interface->GetTransportInstance();
+  delete software_hal_interface;
+  software_hal_interface = nullptr;
   delete a2dp_sink;
-  a2dp_sink = nullptr;
-  session_type = SessionType::UNKNOWN;
+  if (offloading_hal_interface != nullptr) {
+    a2dp_sink = offloading_hal_interface->GetTransportInstance();
+    delete offloading_hal_interface;
+    offloading_hal_interface = nullptr;
+    delete a2dp_sink;
+  }
+
   remote_delay = 0;
 }
 
@@ -637,15 +418,28 @@
     LOG(ERROR) << __func__ << ": BluetoothAudio HAL is not enabled";
     return false;
   }
+  CodecConfiguration codec_config{};
+  if (!a2dp_get_selected_hal_codec_config(&codec_config)) {
+    LOG(ERROR) << __func__ << ": Failed to get CodecConfiguration";
+    return false;
+  }
+  bool should_codec_offloading =
+      bluetooth::audio::codec::IsCodecOffloadingEnabled(codec_config);
+  if (should_codec_offloading && !is_hal_2_0_offloading()) {
+    LOG(WARNING) << __func__ << ": Switching BluetoothAudio HAL to Hardware";
+    end_session();
+    active_hal_interface = offloading_hal_interface;
+  } else if (!should_codec_offloading && is_hal_2_0_offloading()) {
+    LOG(WARNING) << __func__ << ": Switching BluetoothAudio HAL to Software";
+    end_session();
+    active_hal_interface = software_hal_interface;
+  }
+
   AudioConfiguration audio_config{};
-  if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
-    CodecConfiguration codec_config{};
-    if (!a2dp_get_selected_hal_codec_config(&codec_config)) {
-      LOG(ERROR) << __func__ << ": Failed to get CodecConfiguration";
-      return false;
-    }
+  if (active_hal_interface->GetTransportInstance()->GetSessionType() ==
+      SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
     audio_config.codecConfig(codec_config);
-  } else if (session_type == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH) {
+  } else {
     PcmParameters pcm_config{};
     if (!a2dp_get_selected_hal_pcm_config(&pcm_config)) {
       LOG(ERROR) << __func__ << ": Failed to get PcmConfiguration";
@@ -653,7 +447,7 @@
     }
     audio_config.pcmConfig(pcm_config);
   }
-  return a2dp_hal_clientif->UpdateAudioConfig(audio_config);
+  return active_hal_interface->UpdateAudioConfig(audio_config);
 }
 
 void start_session() {
@@ -661,7 +455,7 @@
     LOG(ERROR) << __func__ << ": BluetoothAudio HAL is not enabled";
     return;
   }
-  a2dp_hal_clientif->StartSession();
+  active_hal_interface->StartSession();
 }
 
 void end_session() {
@@ -669,15 +463,19 @@
     LOG(ERROR) << __func__ << ": BluetoothAudio HAL is not enabled";
     return;
   }
-  a2dp_hal_clientif->EndSession();
+  active_hal_interface->EndSession();
+  static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance())
+      ->ResetPresentationPosition();
 }
 
 void ack_stream_started(const tA2DP_CTRL_ACK& ack) {
   auto ctrl_ack = a2dp_ack_to_bt_audio_ctrl_ack(ack);
   LOG(INFO) << __func__ << ": result=" << ctrl_ack;
+  auto a2dp_sink =
+      static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance());
   auto pending_cmd = a2dp_sink->GetPendingCmd();
   if (pending_cmd == A2DP_CTRL_CMD_START) {
-    a2dp_hal_clientif->StreamStarted(ctrl_ack);
+    active_hal_interface->StreamStarted(ctrl_ack);
   } else {
     LOG(WARNING) << __func__ << ": pending=" << pending_cmd
                  << " ignore result=" << ctrl_ack;
@@ -691,9 +489,11 @@
 void ack_stream_suspended(const tA2DP_CTRL_ACK& ack) {
   auto ctrl_ack = a2dp_ack_to_bt_audio_ctrl_ack(ack);
   LOG(INFO) << __func__ << ": result=" << ctrl_ack;
+  auto a2dp_sink =
+      static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance());
   auto pending_cmd = a2dp_sink->GetPendingCmd();
   if (pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
-    a2dp_hal_clientif->StreamSuspended(ctrl_ack);
+    active_hal_interface->StreamSuspended(ctrl_ack);
   } else if (pending_cmd == A2DP_CTRL_CMD_STOP) {
     LOG(INFO) << __func__ << ": A2DP_CTRL_CMD_STOP result=" << ctrl_ack;
   } else {
@@ -711,12 +511,14 @@
   if (!is_hal_2_0_enabled()) {
     LOG(ERROR) << __func__ << ": BluetoothAudio HAL is not enabled";
     return 0;
-  } else if (session_type != SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH) {
-    LOG(ERROR) << __func__ << ": session_type=" << toString(session_type)
+  } else if (is_hal_2_0_offloading()) {
+    LOG(ERROR) << __func__ << ": session_type="
+               << toString(active_hal_interface->GetTransportInstance()
+                               ->GetSessionType())
                << " is not A2DP_SOFTWARE_ENCODING_DATAPATH";
     return 0;
   }
-  return a2dp_hal_clientif->ReadAudioData(p_buf, len);
+  return active_hal_interface->ReadAudioData(p_buf, len);
 }
 
 // Update A2DP delay report to BluetoothAudio HAL
@@ -727,9 +529,10 @@
     remote_delay = delay_report;
     return;
   }
-  LOG(INFO) << __func__ << ": DELAY " << static_cast<float>(delay_report / 10.0)
-            << " ms";
-  a2dp_sink->SetRemoteDelay(delay_report);
+  VLOG(1) << __func__ << ": DELAY " << static_cast<float>(delay_report / 10.0)
+          << " ms";
+  static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance())
+      ->SetRemoteDelay(delay_report);
 }
 
 }  // namespace a2dp
diff --git a/audio_hal_interface/a2dp_encoding.h b/audio_hal_interface/a2dp_encoding.h
index 7b104ff..fbecde7 100644
--- a/audio_hal_interface/a2dp_encoding.h
+++ b/audio_hal_interface/a2dp_encoding.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <vector>
+
 #include "audio_a2dp_hw/include/audio_a2dp_hw.h"
 #include "common/message_loop_thread.h"
 
@@ -23,9 +25,15 @@
 namespace audio {
 namespace a2dp {
 
+bool update_codec_offloading_capabilities(
+    const std::vector<btav_a2dp_codec_config_t>& framework_preference);
+
 // Check if new bluetooth_audio is enabled
 bool is_hal_2_0_enabled();
 
+// Check if new bluetooth_audio is running with offloading encoders
+bool is_hal_2_0_offloading();
+
 // Initialize BluetoothAudio HAL: openProvider
 bool init(bluetooth::common::MessageLoopThread* message_loop);
 
diff --git a/audio_hal_interface/client_interface.cc b/audio_hal_interface/client_interface.cc
index 9f1be24..e14a102 100644
--- a/audio_hal_interface/client_interface.cc
+++ b/audio_hal_interface/client_interface.cc
@@ -191,19 +191,8 @@
                                                              bluetooth::common::MessageLoopThread* message_loop)
     : sink_(sink), provider_(nullptr), session_started_(false), mDataMQ(nullptr),
       death_recipient_(new BluetoothAudioDeathRecipient(this, message_loop)) {
-  auto service_manager = android::hardware::defaultServiceManager1_2();
-  CHECK(service_manager != nullptr);
-  size_t instance_count = 0;
-  auto listManifestByInterface_cb = [&instance_count](const hidl_vec<android::hardware::hidl_string>& instanceNames) {
-    instance_count = instanceNames.size();
-    LOG(INFO) << "listManifestByInterface_cb returns " << instance_count << " instance(s)";
-  };
-  auto hidl_retval = service_manager->listManifestByInterface(kFullyQualifiedInterfaceName, listManifestByInterface_cb);
-  if (!hidl_retval.isOk()) {
-    LOG(FATAL) << __func__ << ": IServiceManager::listByInterface failure: " << hidl_retval.description();
-  }
-  if (instance_count > 0) {
-    fetch_audio_provider();
+  if (IsSupported()) {
+    FetchAudioProvider();
   } else {
     LOG(WARNING) << "IBluetoothAudioProvidersFactory not declared";
   }
@@ -223,7 +212,57 @@
   return capabilities_;
 }
 
-void BluetoothAudioClientInterface::fetch_audio_provider() {
+bool BluetoothAudioClientInterface::IsSupported() {
+  auto service_manager = android::hardware::defaultServiceManager1_2();
+  CHECK(service_manager != nullptr);
+  size_t instance_count = 0;
+  auto listManifestByInterface_cb =
+      [&instance_count](
+          const hidl_vec<android::hardware::hidl_string>& instanceNames) {
+        instance_count = instanceNames.size();
+        LOG(INFO) << "listManifestByInterface_cb returns " << instance_count
+                  << " instance(s)";
+      };
+  auto hidl_retval = service_manager->listManifestByInterface(
+      kFullyQualifiedInterfaceName, listManifestByInterface_cb);
+  if (!hidl_retval.isOk()) {
+    LOG(FATAL) << __func__ << ": IServiceManager::listByInterface failure: "
+               << hidl_retval.description();
+    return false;
+  }
+  return (instance_count > 0);
+}
+
+std::vector<AudioCapabilities>
+BluetoothAudioClientInterface::GetAudioCapabilities(SessionType session_type) {
+  std::vector<AudioCapabilities> capabilities(0);
+  if (!IsSupported()) return capabilities;
+
+  android::sp<IBluetoothAudioProvidersFactory> providersFactory =
+      IBluetoothAudioProvidersFactory::getService();
+  CHECK(providersFactory != nullptr)
+      << "IBluetoothAudioProvidersFactory::getService() failed";
+  LOG(INFO) << "IBluetoothAudioProvidersFactory::getService() returned "
+            << providersFactory.get()
+            << (providersFactory->isRemote() ? " (remote)" : " (local)");
+
+  auto getProviderCapabilities_cb =
+      [&capabilities](const hidl_vec<AudioCapabilities>& audioCapabilities) {
+        for (auto capability : audioCapabilities) {
+          capabilities.push_back(capability);
+        }
+      };
+  auto hidl_retval = providersFactory->getProviderCapabilities(
+      session_type, getProviderCapabilities_cb);
+  if (!hidl_retval.isOk()) {
+    LOG(FATAL) << __func__
+               << ": BluetoothAudioHal::getProviderCapabilities failure: "
+               << hidl_retval.description();
+  }
+  return capabilities;
+}
+
+void BluetoothAudioClientInterface::FetchAudioProvider() {
   if (provider_ != nullptr) {
     LOG(WARNING) << __func__ << ": reflash";
   }
@@ -235,20 +274,16 @@
             << providersFactory.get()
             << (providersFactory->isRemote() ? " (remote)" : " (local)");
 
-  std::promise<void> getProviderCapabilities_promise;
-  auto getProviderCapabilities_future =
-      getProviderCapabilities_promise.get_future();
   auto getProviderCapabilities_cb =
-      [& capabilities = this->capabilities_, &getProviderCapabilities_promise](
+      [& capabilities = this->capabilities_](
           const hidl_vec<AudioCapabilities>& audioCapabilities) {
+        capabilities.clear();
         for (auto capability : audioCapabilities) {
           capabilities.push_back(capability);
         }
-        getProviderCapabilities_promise.set_value();
       };
   auto hidl_retval = providersFactory->getProviderCapabilities(
       sink_->GetSessionType(), getProviderCapabilities_cb);
-  getProviderCapabilities_future.get();
   if (!hidl_retval.isOk()) {
     LOG(FATAL) << __func__ << ": BluetoothAudioHal::getProviderCapabilities failure: " << hidl_retval.description();
     return;
@@ -492,9 +527,12 @@
 void BluetoothAudioClientInterface::RenewAudioProviderAndSession() {
   // NOTE: must be invoked on the same thread where this
   // BluetoothAudioClientInterface is running
-  fetch_audio_provider();
-  session_started_ = false;
-  StartSession();
+  FetchAudioProvider();
+  if (session_started_) {
+    LOG(INFO) << __func__ << ": Restart the session while audio HAL recovering";
+    session_started_ = false;
+    StartSession();
+  }
 }
 
 }  // namespace audio
diff --git a/audio_hal_interface/client_interface.h b/audio_hal_interface/client_interface.h
index 8ee2bb1..1484fc9 100644
--- a/audio_hal_interface/client_interface.h
+++ b/audio_hal_interface/client_interface.h
@@ -18,6 +18,7 @@
 
 #include <time.h>
 #include <mutex>
+#include <vector>
 
 #include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvider.h>
 #include <android/hardware/bluetooth/audio/2.0/types.h>
@@ -35,8 +36,6 @@
 using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
 using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
 using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
-using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
-using ::android::hardware::bluetooth::audio::V2_0::CodecType;
 using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioProvider;
 using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
 using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
@@ -135,7 +134,11 @@
     return provider_ != nullptr;
   }
 
+  IBluetoothTransportInstance* GetTransportInstance() const { return sink_; }
+
   std::vector<AudioCapabilities> GetAudioCapabilities() const;
+  static std::vector<AudioCapabilities> GetAudioCapabilities(
+      SessionType session_type);
 
   bool UpdateAudioConfig(const AudioConfiguration& audioConfig);
 
@@ -163,8 +166,10 @@
   };
 
  private:
+  static bool IsSupported();
+
   // Helper function to connect to an IBluetoothAudioProvider
-  void fetch_audio_provider();
+  void FetchAudioProvider();
 
   mutable std::mutex internal_mutex_;
   IBluetoothTransportInstance* sink_;
diff --git a/audio_hal_interface/client_interface_unittest.cc b/audio_hal_interface/client_interface_unittest.cc
index fd8c8dc..f487f85 100644
--- a/audio_hal_interface/client_interface_unittest.cc
+++ b/audio_hal_interface/client_interface_unittest.cc
@@ -17,6 +17,7 @@
 #include <gtest/gtest.h>
 
 #include "client_interface.h"
+#include "codec_status.h"
 
 namespace {
 
@@ -25,7 +26,6 @@
 using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
 using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
 using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
-using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
 using ::android::hardware::bluetooth::audio::V2_0::CodecType;
 using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
 using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
@@ -38,24 +38,76 @@
 
 using ::bluetooth::audio::AudioCapabilities;
 using ::bluetooth::audio::AudioConfiguration;
-using ::bluetooth::audio::BitsPerSample;
 using ::bluetooth::audio::BluetoothAudioClientInterface;
 using ::bluetooth::audio::BluetoothAudioStatus;
-using ::bluetooth::audio::ChannelMode;
 using ::bluetooth::audio::PcmParameters;
-using ::bluetooth::audio::SampleRate;
 using ::bluetooth::audio::SessionType;
+using ::bluetooth::audio::codec::A2dpCodecToHalBitsPerSample;
+using ::bluetooth::audio::codec::A2dpCodecToHalChannelMode;
+using ::bluetooth::audio::codec::A2dpCodecToHalSampleRate;
+using ::bluetooth::audio::codec::BitsPerSample;
+using ::bluetooth::audio::codec::ChannelMode;
+using ::bluetooth::audio::codec::CodecConfiguration;
+using ::bluetooth::audio::codec::IsCodecOffloadingEnabled;
+using ::bluetooth::audio::codec::SampleRate;
+using ::bluetooth::audio::codec::UpdateOffloadingCapabilities;
 using ::testing::Test;
 
-constexpr SampleRate kSampleRates[9] = {
-    SampleRate::RATE_UNKNOWN, SampleRate::RATE_44100, SampleRate::RATE_48000,
-    SampleRate::RATE_88200,   SampleRate::RATE_96000, SampleRate::RATE_176400,
-    SampleRate::RATE_192000,  SampleRate::RATE_16000, SampleRate::RATE_24000};
-constexpr BitsPerSample kBitsPerSamples[4] = {
-    BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16, BitsPerSample::BITS_24,
-    BitsPerSample::BITS_32};
-constexpr ChannelMode kChannelModes[3] = {
-    ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+struct SampleRatePair {
+  SampleRate hal_sample_rate_;
+  btav_a2dp_codec_sample_rate_t btav_sample_rate_;
+};
+constexpr SampleRatePair kSampleRatePairs[9] = {
+    {.hal_sample_rate_ = SampleRate::RATE_UNKNOWN,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE},
+    {.hal_sample_rate_ = SampleRate::RATE_44100,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_44100},
+    {.hal_sample_rate_ = SampleRate::RATE_48000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_48000},
+    {.hal_sample_rate_ = SampleRate::RATE_88200,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_88200},
+    {.hal_sample_rate_ = SampleRate::RATE_96000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_96000},
+    {.hal_sample_rate_ = SampleRate::RATE_176400,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_176400},
+    {.hal_sample_rate_ = SampleRate::RATE_192000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_192000},
+    {.hal_sample_rate_ = SampleRate::RATE_16000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_16000},
+    {.hal_sample_rate_ = SampleRate::RATE_24000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_24000}};
+
+struct BitsPerSamplePair {
+  BitsPerSample hal_bits_per_sample_;
+  btav_a2dp_codec_bits_per_sample_t btav_bits_per_sample_;
+};
+constexpr BitsPerSamplePair kBitsPerSamplePairs[4] = {
+    {.hal_bits_per_sample_ = BitsPerSample::BITS_UNKNOWN,
+     .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE},
+    {.hal_bits_per_sample_ = BitsPerSample::BITS_16,
+     .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16},
+    {.hal_bits_per_sample_ = BitsPerSample::BITS_24,
+     .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24},
+    {.hal_bits_per_sample_ = BitsPerSample::BITS_32,
+     .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32}};
+
+struct ChannelModePair {
+  ChannelMode hal_channel_mode_;
+  btav_a2dp_codec_channel_mode_t btav_channel_mode_;
+};
+constexpr ChannelModePair kChannelModePairs[3] = {
+    {.hal_channel_mode_ = ChannelMode::UNKNOWN,
+     .btav_channel_mode_ = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE},
+    {.hal_channel_mode_ = ChannelMode::MONO,
+     .btav_channel_mode_ = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO},
+    {.hal_channel_mode_ = ChannelMode::STEREO,
+     .btav_channel_mode_ = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO}};
+
+constexpr btav_a2dp_codec_index_t codec_indexes[] = {
+    BTAV_A2DP_CODEC_INDEX_SOURCE_SBC,  BTAV_A2DP_CODEC_INDEX_SOURCE_AAC,
+    BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD,
+    BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, BTAV_A2DP_CODEC_INDEX_SINK_SBC,
+    BTAV_A2DP_CODEC_INDEX_SINK_AAC,    BTAV_A2DP_CODEC_INDEX_SINK_LDAC};
 constexpr uint16_t kPeerMtus[5] = {660, 663, 883, 1005, 1500};
 
 class TestTransport : public bluetooth::audio::IBluetoothTransportInstance {
@@ -94,15 +146,17 @@
 
 class BluetoothAudioClientInterfaceTest : public Test {
  protected:
-  TestTransport* test_transport_;
-  BluetoothAudioClientInterface* clientif_;
+  TestTransport* test_transport_ = nullptr;
+  BluetoothAudioClientInterface* clientif_ = nullptr;
 
   static constexpr int kClientIfReturnSuccess = 0;
 
   void SetUp() override {}
 
   void TearDown() override {
+    if (clientif_ != nullptr) delete clientif_;
     clientif_ = nullptr;
+    if (test_transport_ != nullptr) delete test_transport_;
     test_transport_ = nullptr;
   }
 
@@ -121,8 +175,7 @@
     return (is_pcm_config_valid && is_pcm_config_supported);
   }
 
-  bool IsOffloadCodecConfigurationSupported(
-      const CodecConfiguration& codec_config) {
+  bool IsCodecOffloadingSupported(const CodecConfiguration& codec_config) {
     CodecCapabilities codec_capability = {};
     for (auto audio_capability : clientif_->GetAudioCapabilities()) {
       if (audio_capability.codecCapabilities().codecType ==
@@ -197,18 +250,38 @@
 
 }  // namespace
 
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpCodecToHalPcmConfig) {
+  btav_a2dp_codec_config_t a2dp_codec_config = {};
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    a2dp_codec_config.sample_rate = sample_rate_pair.btav_sample_rate_;
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      a2dp_codec_config.bits_per_sample =
+          bits_per_sample_pair.btav_bits_per_sample_;
+      for (auto channel_mode_pair : kChannelModePairs) {
+        a2dp_codec_config.channel_mode = channel_mode_pair.btav_channel_mode_;
+        EXPECT_EQ(A2dpCodecToHalSampleRate(a2dp_codec_config),
+                  sample_rate_pair.hal_sample_rate_);
+        EXPECT_EQ(A2dpCodecToHalBitsPerSample(a2dp_codec_config),
+                  bits_per_sample_pair.hal_bits_per_sample_);
+        EXPECT_EQ(A2dpCodecToHalChannelMode(a2dp_codec_config),
+                  channel_mode_pair.hal_channel_mode_);
+      }  // ChannelMode
+    }    // BitsPerSampple
+  }      // SampleRate
+}
+
 TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpSoftwareSession) {
   test_transport_ =
       new TestTransport(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
   PcmParameters pcm_config = {};
-  for (auto sample_rate : kSampleRates) {
-    pcm_config.sampleRate = sample_rate;
-    for (auto bits_per_sample : kBitsPerSamples) {
-      pcm_config.bitsPerSample = bits_per_sample;
-      for (auto channel_mode : kChannelModes) {
-        pcm_config.channelMode = channel_mode;
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    pcm_config.sampleRate = sample_rate_pair.hal_sample_rate_;
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      pcm_config.bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_;
+      for (auto channel_mode_pair : kChannelModePairs) {
+        pcm_config.channelMode = channel_mode_pair.hal_channel_mode_;
         audio_config.pcmConfig(pcm_config);
         clientif_->UpdateAudioConfig(audio_config);
         if (IsSoftwarePcmParametersSupported(pcm_config)) {
@@ -222,11 +295,36 @@
   }      // SampleRate
 }
 
-TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadSbcSession) {
-  test_transport_ =
-      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
-  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
-  AudioConfiguration audio_config = {};
+struct CodecOffloadingPreference {
+  bool is_target_codec_included_;
+  std::vector<btav_a2dp_codec_config_t> preference_;
+};
+
+std::vector<CodecOffloadingPreference> CodecOffloadingPreferenceGenerator(
+    btav_a2dp_codec_index_t target_codec_index) {
+  std::vector<CodecOffloadingPreference> codec_offloading_preferences = {
+      {.is_target_codec_included_ = false,
+       .preference_ = std::vector<btav_a2dp_codec_config_t>(0)}};
+  btav_a2dp_codec_config_t a2dp_codec_config = {};
+  for (auto codec_index : codec_indexes) {
+    a2dp_codec_config.codec_type = codec_index;
+    auto duplicated_preferences = codec_offloading_preferences;
+    for (auto iter = duplicated_preferences.begin();
+         iter != duplicated_preferences.end(); ++iter) {
+      if (codec_index == target_codec_index) {
+        iter->is_target_codec_included_ = true;
+      }
+      iter->preference_.push_back(a2dp_codec_config);
+    }
+    codec_offloading_preferences.insert(codec_offloading_preferences.end(),
+                                        duplicated_preferences.begin(),
+                                        duplicated_preferences.end());
+  }
+  return codec_offloading_preferences;
+}
+
+std::vector<CodecConfiguration> SbcCodecConfigurationsGenerator() {
+  std::vector<CodecConfiguration> sbc_codec_configs;
   CodecConfiguration codec_config = {};
   SbcBlockLength block_lengths[4] = {
       SbcBlockLength::BLOCKS_4, SbcBlockLength::BLOCKS_8,
@@ -235,9 +333,9 @@
                                     SbcNumSubbands::SUBBAND_8};
   SbcAllocMethod alloc_methods[2] = {SbcAllocMethod::ALLOC_MD_S,
                                      SbcAllocMethod::ALLOC_MD_L};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      for (auto channel_mode_pair : kChannelModePairs) {
         for (auto peer_mtu : kPeerMtus) {
           for (auto block_length : block_lengths) {
             for (auto num_subband : num_subbands) {
@@ -248,25 +346,19 @@
                 // A2DP_SBC_DEFAULT_BITRATE
                 codec_config.encodedAudioBitrate = 328000;
                 SbcParameters sbc = {
-                    .sampleRate = sample_rate,
-                    .channelMode = (channel_mode == ChannelMode::MONO
+                    .sampleRate = sample_rate_pair.hal_sample_rate_,
+                    .channelMode = (channel_mode_pair.hal_channel_mode_ ==
+                                            ChannelMode::MONO
                                         ? SbcChannelMode::MONO
                                         : SbcChannelMode::JOINT_STEREO),
                     .blockLength = block_length,
                     .numSubbands = num_subband,
                     .allocMethod = alloc_method,
-                    .bitsPerSample = bits_per_sample,
+                    .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_,
                     .minBitpool = 2,
                     .maxBitpool = 53};
                 codec_config.config.sbcConfig(sbc);
-                audio_config.codecConfig(codec_config);
-                clientif_->UpdateAudioConfig(audio_config);
-                if (IsOffloadCodecConfigurationSupported(codec_config)) {
-                  EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-                } else {
-                  EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-                }
-                EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+                sbc_codec_configs.push_back(codec_config);
               }  // SbcAllocMethod
             }    // SbcNumSubbands
           }      // SbcBlockLength
@@ -274,22 +366,56 @@
       }          // ChannelMode
     }            // BitsPerSampple
   }              // SampleRate
+  return sbc_codec_configs;
 }
 
-TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAacSession) {
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpSbcCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto sbc_codec_configs = SbcCodecConfigurationsGenerator();
+  for (auto codec_offloading_preference :
+       CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : sbc_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadSbcSession) {
   test_transport_ =
       new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
+  for (CodecConfiguration codec_config : SbcCodecConfigurationsGenerator()) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
+}
+
+std::vector<CodecConfiguration> AacCodecConfigurationsGenerator() {
+  std::vector<CodecConfiguration> aac_codec_configs;
   CodecConfiguration codec_config = {};
   AacObjectType object_types[4] = {
       AacObjectType::MPEG2_LC, AacObjectType::MPEG4_LC,
       AacObjectType::MPEG4_LTP, AacObjectType::MPEG4_SCALABLE};
   AacVariableBitRate variable_bitrates[2] = {AacVariableBitRate::DISABLED,
                                              AacVariableBitRate::ENABLED};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      for (auto channel_mode_pair : kChannelModePairs) {
         for (auto peer_mtu : kPeerMtus) {
           for (auto object_type : object_types) {
             for (auto variable_bitrate : variable_bitrates) {
@@ -298,40 +424,68 @@
               codec_config.isScmstEnabled = false;
               // A2DP_AAC_DEFAULT_BITRATE
               codec_config.encodedAudioBitrate = 320000;
-              AacParameters aac = {.objectType = object_type,
-                                   .sampleRate = sample_rate,
-                                   .channelMode = channel_mode,
-                                   .variableBitRateEnabled = variable_bitrate,
-                                   .bitsPerSample = bits_per_sample};
+              AacParameters aac = {
+                  .objectType = object_type,
+                  .sampleRate = sample_rate_pair.hal_sample_rate_,
+                  .channelMode = channel_mode_pair.hal_channel_mode_,
+                  .variableBitRateEnabled = variable_bitrate,
+                  .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_};
               codec_config.config.aacConfig(aac);
-              audio_config.codecConfig(codec_config);
-              clientif_->UpdateAudioConfig(audio_config);
-              if (IsOffloadCodecConfigurationSupported(codec_config)) {
-                EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-              } else {
-                EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-              }
-              EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+              aac_codec_configs.push_back(codec_config);
             }  // AacVariableBitRate
           }    // AacObjectType
         }      // peerMtu
       }        // ChannelMode
     }          // BitsPerSampple
   }            // SampleRate
+  return aac_codec_configs;
 }
 
-TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadLdacSession) {
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpAacCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto aac_codec_configs = AacCodecConfigurationsGenerator();
+  for (auto codec_offloading_preference :
+       CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : aac_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAacSession) {
   test_transport_ =
       new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
+  for (CodecConfiguration codec_config : AacCodecConfigurationsGenerator()) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
+}
+
+std::vector<CodecConfiguration> LdacCodecConfigurationsGenerator() {
+  std::vector<CodecConfiguration> ldac_codec_configs;
   CodecConfiguration codec_config = {};
   LdacQualityIndex quality_indexes[4] = {
       LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID,
       LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      for (auto channel_mode_pair : kChannelModePairs) {
         for (auto peer_mtu : kPeerMtus) {
           for (auto quality_index : quality_indexes) {
             codec_config.codecType = CodecType::LDAC;
@@ -339,26 +493,104 @@
             codec_config.isScmstEnabled = false;
             codec_config.encodedAudioBitrate = 990000;
             LdacParameters ldac = {
-                .sampleRate = sample_rate,
-                .channelMode = (channel_mode == ChannelMode::MONO
-                                    ? LdacChannelMode::MONO
-                                    : LdacChannelMode::STEREO),
+                .sampleRate = sample_rate_pair.hal_sample_rate_,
+                .channelMode =
+                    (channel_mode_pair.hal_channel_mode_ == ChannelMode::MONO
+                         ? LdacChannelMode::MONO
+                         : LdacChannelMode::STEREO),
                 .qualityIndex = quality_index,
-                .bitsPerSample = bits_per_sample};
+                .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_};
             codec_config.config.ldacConfig(ldac);
-            audio_config.codecConfig(codec_config);
-            clientif_->UpdateAudioConfig(audio_config);
-            if (IsOffloadCodecConfigurationSupported(codec_config)) {
-              EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-            } else {
-              EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-            }
-            EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+            ldac_codec_configs.push_back(codec_config);
           }  // LdacQualityIndex
         }    // peerMtu
       }      // ChannelMode
     }        // BitsPerSampple
   }          // SampleRate
+  return ldac_codec_configs;
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpLdacCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto ldac_codec_configs = LdacCodecConfigurationsGenerator();
+  for (auto codec_offloading_preference :
+       CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : ldac_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadLdacSession) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  AudioConfiguration audio_config = {};
+  for (CodecConfiguration codec_config : LdacCodecConfigurationsGenerator()) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
+}
+
+std::vector<CodecConfiguration> AptxCodecConfigurationsGenerator(
+    CodecType codec_type) {
+  std::vector<CodecConfiguration> aptx_codec_configs;
+  if (codec_type != CodecType::APTX && codec_type != CodecType::APTX_HD)
+    return aptx_codec_configs;
+  CodecConfiguration codec_config = {};
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      for (auto channel_mode_pair : kChannelModePairs) {
+        for (auto peer_mtu : kPeerMtus) {
+          codec_config.codecType = codec_type;
+          codec_config.peerMtu = peer_mtu;
+          codec_config.isScmstEnabled = false;
+          codec_config.encodedAudioBitrate =
+              (codec_type == CodecType::APTX ? 352000 : 576000);
+          AptxParameters aptx = {
+              .sampleRate = sample_rate_pair.hal_sample_rate_,
+              .channelMode = channel_mode_pair.hal_channel_mode_,
+              .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_};
+          codec_config.config.aptxConfig(aptx);
+          aptx_codec_configs.push_back(codec_config);
+        }  // peerMtu
+      }    // ChannelMode
+    }      // BitsPerSampple
+  }        // SampleRate
+  return aptx_codec_configs;
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpAptxCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto aptx_codec_configs = AptxCodecConfigurationsGenerator(CodecType::APTX);
+  for (auto codec_offloading_preference :
+       CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : aptx_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
 }
 
 TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAptxSession) {
@@ -366,31 +598,37 @@
       new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
-  CodecConfiguration codec_config = {};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
-        for (auto peer_mtu : kPeerMtus) {
-          codec_config.codecType = CodecType::APTX;
-          codec_config.peerMtu = peer_mtu;
-          codec_config.isScmstEnabled = false;
-          codec_config.encodedAudioBitrate = 352000;
-          AptxParameters aptx = {.sampleRate = sample_rate,
-                                 .channelMode = channel_mode,
-                                 .bitsPerSample = bits_per_sample};
-          codec_config.config.aptxConfig(aptx);
-          audio_config.codecConfig(codec_config);
-          clientif_->UpdateAudioConfig(audio_config);
-          if (IsOffloadCodecConfigurationSupported(codec_config)) {
-            EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-          } else {
-            EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-          }
-          EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
-        }  // peerMtu
-      }    // ChannelMode
-    }      // BitsPerSampple
-  }        // SampleRate
+  for (CodecConfiguration codec_config :
+       AptxCodecConfigurationsGenerator(CodecType::APTX)) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpAptxHdCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto aptx_hd_codec_configs =
+      AptxCodecConfigurationsGenerator(CodecType::APTX_HD);
+  for (auto codec_offloading_preference : CodecOffloadingPreferenceGenerator(
+           BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : aptx_hd_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
 }
 
 TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAptxHdSession) {
@@ -398,31 +636,17 @@
       new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
-  CodecConfiguration codec_config = {};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
-        for (auto peer_mtu : kPeerMtus) {
-          codec_config.codecType = CodecType::APTX_HD;
-          codec_config.peerMtu = peer_mtu;
-          codec_config.isScmstEnabled = false;
-          codec_config.encodedAudioBitrate = 576000;
-          AptxParameters aptx = {.sampleRate = sample_rate,
-                                 .channelMode = channel_mode,
-                                 .bitsPerSample = bits_per_sample};
-          codec_config.config.aptxConfig(aptx);
-          audio_config.codecConfig(codec_config);
-          clientif_->UpdateAudioConfig(audio_config);
-          if (IsOffloadCodecConfigurationSupported(codec_config)) {
-            EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-          } else {
-            EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-          }
-          EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
-        }  // peerMtu
-      }    // ChannelMode
-    }      // BitsPerSampple
-  }        // SampleRate
+  for (CodecConfiguration codec_config :
+       AptxCodecConfigurationsGenerator(CodecType::APTX_HD)) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
 }
 
 TEST_F(BluetoothAudioClientInterfaceTest,
@@ -439,7 +663,7 @@
   codec_config.config = {};
   audio_config.codecConfig(codec_config);
   clientif_->UpdateAudioConfig(audio_config);
-  if (IsOffloadCodecConfigurationSupported(codec_config)) {
+  if (IsCodecOffloadingSupported(codec_config)) {
     EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
   } else {
     EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
@@ -454,12 +678,12 @@
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
   PcmParameters pcm_config = {};
-  for (auto sample_rate : kSampleRates) {
-    pcm_config.sampleRate = sample_rate;
-    for (auto bits_per_sample : kBitsPerSamples) {
-      pcm_config.bitsPerSample = bits_per_sample;
-      for (auto channel_mode : kChannelModes) {
-        pcm_config.channelMode = channel_mode;
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    pcm_config.sampleRate = sample_rate_pair.hal_sample_rate_;
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      pcm_config.bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_;
+      for (auto channel_mode_pair : kChannelModePairs) {
+        pcm_config.channelMode = channel_mode_pair.hal_channel_mode_;
         audio_config.pcmConfig(pcm_config);
         clientif_->UpdateAudioConfig(audio_config);
         if (IsSoftwarePcmParametersSupported(pcm_config)) {
diff --git a/audio_hal_interface/codec_status.cc b/audio_hal_interface/codec_status.cc
new file mode 100644
index 0000000..0819e5c
--- /dev/null
+++ b/audio_hal_interface/codec_status.cc
@@ -0,0 +1,569 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "codec_status.h"
+#include "client_interface.h"
+
+#include "a2dp_aac_constants.h"
+#include "a2dp_sbc_constants.h"
+#include "a2dp_vendor_aptx_constants.h"
+#include "a2dp_vendor_aptx_hd_constants.h"
+#include "a2dp_vendor_ldac_constants.h"
+#include "bta/av/bta_av_int.h"
+
+namespace {
+
+using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
+using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
+using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AudioCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
+using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
+using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
+
+// capabilities from BluetoothAudioClientInterface::GetAudioCapabilities()
+std::vector<AudioCapabilities> audio_hal_capabilities(0);
+// capabilities that audio HAL supports and frameworks / Bluetooth SoC / runtime
+// preference would like to use.
+std::vector<AudioCapabilities> offloading_preference(0);
+
+bool sbc_offloading_capability_match(const SbcParameters& sbc_capability,
+                                     const SbcParameters& sbc_config) {
+  if ((static_cast<SampleRate>(sbc_capability.sampleRate &
+                               sbc_config.sampleRate) ==
+       SampleRate::RATE_UNKNOWN) ||
+      (static_cast<SbcChannelMode>(sbc_capability.channelMode &
+                                   sbc_config.channelMode) ==
+       SbcChannelMode::UNKNOWN) ||
+      (static_cast<SbcBlockLength>(sbc_capability.blockLength &
+                                   sbc_config.blockLength) ==
+       static_cast<SbcBlockLength>(0)) ||
+      (static_cast<SbcNumSubbands>(sbc_capability.numSubbands &
+                                   sbc_config.numSubbands) ==
+       static_cast<SbcNumSubbands>(0)) ||
+      (static_cast<SbcAllocMethod>(sbc_capability.allocMethod &
+                                   sbc_config.allocMethod) ==
+       static_cast<SbcAllocMethod>(0)) ||
+      (static_cast<BitsPerSample>(sbc_capability.bitsPerSample &
+                                  sbc_config.bitsPerSample) ==
+       BitsPerSample::BITS_UNKNOWN) ||
+      (sbc_config.minBitpool < sbc_capability.minBitpool ||
+       sbc_config.maxBitpool < sbc_config.minBitpool ||
+       sbc_capability.maxBitpool < sbc_config.maxBitpool)) {
+    LOG(WARNING) << __func__ << ": software codec=" << toString(sbc_config)
+                 << " capability=" << toString(sbc_capability);
+    return false;
+  }
+  VLOG(1) << __func__ << ": offloading codec=" << toString(sbc_config)
+          << " capability=" << toString(sbc_capability);
+  return true;
+}
+
+bool aac_offloading_capability_match(const AacParameters& aac_capability,
+                                     const AacParameters& aac_config) {
+  if ((static_cast<AacObjectType>(aac_capability.objectType &
+                                  aac_config.objectType) ==
+       static_cast<AacObjectType>(0)) ||
+      (static_cast<SampleRate>(aac_capability.sampleRate &
+                               aac_config.sampleRate) ==
+       SampleRate::RATE_UNKNOWN) ||
+      (static_cast<ChannelMode>(aac_capability.channelMode &
+                                aac_config.channelMode) ==
+       ChannelMode::UNKNOWN) ||
+      (aac_capability.variableBitRateEnabled != AacVariableBitRate::ENABLED &&
+       aac_config.variableBitRateEnabled != AacVariableBitRate::DISABLED) ||
+      (static_cast<BitsPerSample>(aac_capability.bitsPerSample &
+                                  aac_config.bitsPerSample) ==
+       BitsPerSample::BITS_UNKNOWN)) {
+    LOG(WARNING) << __func__ << ": software codec=" << toString(aac_config)
+                 << " capability=" << toString(aac_capability);
+    return false;
+  }
+  VLOG(1) << __func__ << ": offloading codec=" << toString(aac_config)
+          << " capability=" << toString(aac_capability);
+  return true;
+}
+
+bool aptx_offloading_capability_match(const AptxParameters& aptx_capability,
+                                      const AptxParameters& aptx_config) {
+  if ((static_cast<SampleRate>(aptx_capability.sampleRate &
+                               aptx_config.sampleRate) ==
+       SampleRate::RATE_UNKNOWN) ||
+      (static_cast<ChannelMode>(aptx_capability.channelMode &
+                                aptx_config.channelMode) ==
+       ChannelMode::UNKNOWN) ||
+      (static_cast<BitsPerSample>(aptx_capability.bitsPerSample &
+                                  aptx_config.bitsPerSample) ==
+       BitsPerSample::BITS_UNKNOWN)) {
+    LOG(WARNING) << __func__ << ": software codec=" << toString(aptx_config)
+                 << " capability=" << toString(aptx_capability);
+    return false;
+  }
+  VLOG(1) << __func__ << ": offloading codec=" << toString(aptx_config)
+          << " capability=" << toString(aptx_capability);
+  return true;
+}
+
+bool ldac_offloading_capability_match(const LdacParameters& ldac_capability,
+                                      const LdacParameters& ldac_config) {
+  if ((static_cast<SampleRate>(ldac_capability.sampleRate &
+                               ldac_config.sampleRate) ==
+       SampleRate::RATE_UNKNOWN) ||
+      (static_cast<LdacChannelMode>(ldac_capability.channelMode &
+                                    ldac_config.channelMode) ==
+       LdacChannelMode::UNKNOWN) ||
+      (static_cast<BitsPerSample>(ldac_capability.bitsPerSample &
+                                  ldac_config.bitsPerSample) ==
+       BitsPerSample::BITS_UNKNOWN)) {
+    LOG(WARNING) << __func__ << ": software codec=" << toString(ldac_config)
+                 << " capability=" << toString(ldac_capability);
+    return false;
+  }
+  VLOG(1) << __func__ << ": offloading codec=" << toString(ldac_config)
+          << " capability=" << toString(ldac_capability);
+  return true;
+}
+}  // namespace
+
+namespace bluetooth {
+namespace audio {
+namespace codec {
+
+const CodecConfiguration kInvalidCodecConfiguration = {
+    .codecType = CodecType::UNKNOWN,
+    .encodedAudioBitrate = 0x00000000,
+    .peerMtu = 0xffff,
+    .isScmstEnabled = false,
+    .config = {}};
+
+SampleRate A2dpCodecToHalSampleRate(
+    const btav_a2dp_codec_config_t& a2dp_codec_config) {
+  switch (a2dp_codec_config.sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+      return SampleRate::RATE_44100;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      return SampleRate::RATE_48000;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+      return SampleRate::RATE_88200;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+      return SampleRate::RATE_96000;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+      return SampleRate::RATE_176400;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+      return SampleRate::RATE_192000;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_16000:
+      return SampleRate::RATE_16000;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_24000:
+      return SampleRate::RATE_24000;
+    default:
+      return SampleRate::RATE_UNKNOWN;
+  }
+}
+
+BitsPerSample A2dpCodecToHalBitsPerSample(
+    const btav_a2dp_codec_config_t& a2dp_codec_config) {
+  switch (a2dp_codec_config.bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      return BitsPerSample::BITS_16;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+      return BitsPerSample::BITS_24;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+      return BitsPerSample::BITS_32;
+    default:
+      return BitsPerSample::BITS_UNKNOWN;
+  }
+}
+
+ChannelMode A2dpCodecToHalChannelMode(
+    const btav_a2dp_codec_config_t& a2dp_codec_config) {
+  switch (a2dp_codec_config.channel_mode) {
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+      return ChannelMode::MONO;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+      return ChannelMode::STEREO;
+    default:
+      return ChannelMode::UNKNOWN;
+  }
+}
+
+bool A2dpSbcToHalConfig(CodecConfiguration* codec_config,
+                        A2dpCodecConfig* a2dp_config) {
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
+  if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_SBC &&
+      current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SINK_SBC) {
+    *codec_config = {};
+    return false;
+  }
+  tBT_A2DP_OFFLOAD a2dp_offload;
+  a2dp_config->getCodecSpecificConfig(&a2dp_offload);
+  codec_config->codecType = CodecType::SBC;
+  codec_config->config.sbcConfig({});
+  auto sbc_config = codec_config->config.sbcConfig();
+  sbc_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  if (sbc_config.sampleRate == SampleRate::RATE_UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown SBC sample_rate=" << current_codec.sample_rate;
+    return false;
+  }
+  uint8_t channel_mode = a2dp_offload.codec_info[3] & A2DP_SBC_IE_CH_MD_MSK;
+  switch (channel_mode) {
+    case A2DP_SBC_IE_CH_MD_JOINT:
+      sbc_config.channelMode = SbcChannelMode::JOINT_STEREO;
+      break;
+    case A2DP_SBC_IE_CH_MD_STEREO:
+      sbc_config.channelMode = SbcChannelMode::STEREO;
+      break;
+    case A2DP_SBC_IE_CH_MD_DUAL:
+      sbc_config.channelMode = SbcChannelMode::DUAL;
+      break;
+    case A2DP_SBC_IE_CH_MD_MONO:
+      sbc_config.channelMode = SbcChannelMode::MONO;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown SBC channel_mode=" << channel_mode;
+      sbc_config.channelMode = SbcChannelMode::UNKNOWN;
+      return false;
+  }
+  uint8_t block_length = a2dp_offload.codec_info[0] & A2DP_SBC_IE_BLOCKS_MSK;
+  switch (block_length) {
+    case A2DP_SBC_IE_BLOCKS_4:
+      sbc_config.blockLength = SbcBlockLength::BLOCKS_4;
+      break;
+    case A2DP_SBC_IE_BLOCKS_8:
+      sbc_config.blockLength = SbcBlockLength::BLOCKS_8;
+      break;
+    case A2DP_SBC_IE_BLOCKS_12:
+      sbc_config.blockLength = SbcBlockLength::BLOCKS_12;
+      break;
+    case A2DP_SBC_IE_BLOCKS_16:
+      sbc_config.blockLength = SbcBlockLength::BLOCKS_16;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown SBC block_length=" << block_length;
+      return false;
+  }
+  uint8_t sub_bands = a2dp_offload.codec_info[0] & A2DP_SBC_IE_SUBBAND_MSK;
+  switch (sub_bands) {
+    case A2DP_SBC_IE_SUBBAND_4:
+      sbc_config.numSubbands = SbcNumSubbands::SUBBAND_4;
+      break;
+    case A2DP_SBC_IE_SUBBAND_8:
+      sbc_config.numSubbands = SbcNumSubbands::SUBBAND_8;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown SBC Subbands=" << sub_bands;
+      return false;
+  }
+  uint8_t alloc_method = a2dp_offload.codec_info[0] & A2DP_SBC_IE_ALLOC_MD_MSK;
+  switch (alloc_method) {
+    case A2DP_SBC_IE_ALLOC_MD_S:
+      sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_S;
+      break;
+    case A2DP_SBC_IE_ALLOC_MD_L:
+      sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_L;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown SBC alloc_method=" << alloc_method;
+      return false;
+  }
+  sbc_config.minBitpool = a2dp_offload.codec_info[1];
+  sbc_config.maxBitpool = a2dp_offload.codec_info[2];
+  sbc_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  if (sbc_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
+    LOG(ERROR) << __func__ << ": Unknown SBC bits_per_sample="
+               << current_codec.bits_per_sample;
+    return false;
+  }
+  codec_config->config.sbcConfig(sbc_config);
+  return true;
+}
+
+bool A2dpAacToHalConfig(CodecConfiguration* codec_config,
+                        A2dpCodecConfig* a2dp_config) {
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
+  if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_AAC &&
+      current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SINK_AAC) {
+    *codec_config = {};
+    return false;
+  }
+  tBT_A2DP_OFFLOAD a2dp_offload;
+  a2dp_config->getCodecSpecificConfig(&a2dp_offload);
+  codec_config->codecType = CodecType::AAC;
+  codec_config->config.aacConfig({});
+  auto aac_config = codec_config->config.aacConfig();
+  uint8_t object_type = a2dp_offload.codec_info[0];
+  switch (object_type) {
+    case A2DP_AAC_OBJECT_TYPE_MPEG2_LC:
+      aac_config.objectType = AacObjectType::MPEG2_LC;
+      break;
+    case A2DP_AAC_OBJECT_TYPE_MPEG4_LC:
+      aac_config.objectType = AacObjectType::MPEG4_LC;
+      break;
+    case A2DP_AAC_OBJECT_TYPE_MPEG4_LTP:
+      aac_config.objectType = AacObjectType::MPEG4_LTP;
+      break;
+    case A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE:
+      aac_config.objectType = AacObjectType::MPEG4_SCALABLE;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown AAC object_type=" << +object_type;
+      return false;
+  }
+  aac_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  if (aac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown AAC sample_rate=" << current_codec.sample_rate;
+    return false;
+  }
+  aac_config.channelMode = A2dpCodecToHalChannelMode(current_codec);
+  if (aac_config.channelMode == ChannelMode::UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown AAC channel_mode=" << current_codec.channel_mode;
+    return false;
+  }
+  uint8_t vbr_enabled =
+      a2dp_offload.codec_info[1] & A2DP_AAC_VARIABLE_BIT_RATE_MASK;
+  switch (vbr_enabled) {
+    case A2DP_AAC_VARIABLE_BIT_RATE_ENABLED:
+      aac_config.variableBitRateEnabled = AacVariableBitRate::ENABLED;
+      break;
+    case A2DP_AAC_VARIABLE_BIT_RATE_DISABLED:
+      aac_config.variableBitRateEnabled = AacVariableBitRate::DISABLED;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown AAC VBR=" << +vbr_enabled;
+      return false;
+  }
+  aac_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  if (aac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
+    LOG(ERROR) << __func__ << ": Unknown AAC bits_per_sample="
+               << current_codec.bits_per_sample;
+    return false;
+  }
+  codec_config->config.aacConfig(aac_config);
+  return true;
+}
+
+bool A2dpAptxToHalConfig(CodecConfiguration* codec_config,
+                         A2dpCodecConfig* a2dp_config) {
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
+  if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_APTX &&
+      current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD) {
+    *codec_config = {};
+    return false;
+  }
+  tBT_A2DP_OFFLOAD a2dp_offload;
+  a2dp_config->getCodecSpecificConfig(&a2dp_offload);
+  if (current_codec.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX) {
+    codec_config->codecType = CodecType::APTX;
+  } else {
+    codec_config->codecType = CodecType::APTX_HD;
+  }
+  codec_config->config.aptxConfig({});
+  auto aptx_config = codec_config->config.aptxConfig();
+  aptx_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  if (aptx_config.sampleRate == SampleRate::RATE_UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown aptX sample_rate=" << current_codec.sample_rate;
+    return false;
+  }
+  aptx_config.channelMode = A2dpCodecToHalChannelMode(current_codec);
+  if (aptx_config.channelMode == ChannelMode::UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown aptX channel_mode=" << current_codec.channel_mode;
+    return false;
+  }
+  aptx_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  if (aptx_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
+    LOG(ERROR) << __func__ << ": Unknown aptX bits_per_sample="
+               << current_codec.bits_per_sample;
+    return false;
+  }
+  codec_config->config.aptxConfig(aptx_config);
+  return true;
+}
+
+bool A2dpLdacToHalConfig(CodecConfiguration* codec_config,
+                         A2dpCodecConfig* a2dp_config) {
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
+  if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC) {
+    codec_config = {};
+    return false;
+  }
+  tBT_A2DP_OFFLOAD a2dp_offload;
+  a2dp_config->getCodecSpecificConfig(&a2dp_offload);
+  codec_config->codecType = CodecType::LDAC;
+  codec_config->config.ldacConfig({});
+  auto ldac_config = codec_config->config.ldacConfig();
+  ldac_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  if (ldac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown LDAC sample_rate=" << current_codec.sample_rate;
+    return false;
+  }
+  switch (a2dp_offload.codec_info[7]) {
+    case A2DP_LDAC_CHANNEL_MODE_STEREO:
+      ldac_config.channelMode = LdacChannelMode::STEREO;
+      break;
+    case A2DP_LDAC_CHANNEL_MODE_DUAL:
+      ldac_config.channelMode = LdacChannelMode::DUAL;
+      break;
+    case A2DP_LDAC_CHANNEL_MODE_MONO:
+      ldac_config.channelMode = LdacChannelMode::MONO;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown LDAC channel_mode="
+                 << a2dp_offload.codec_info[7];
+      ldac_config.channelMode = LdacChannelMode::UNKNOWN;
+      return false;
+  }
+  switch (a2dp_offload.codec_info[6]) {
+    case A2DP_LDAC_QUALITY_HIGH:
+      ldac_config.qualityIndex = LdacQualityIndex::QUALITY_HIGH;
+      break;
+    case A2DP_LDAC_QUALITY_MID:
+      ldac_config.qualityIndex = LdacQualityIndex::QUALITY_MID;
+      break;
+    case A2DP_LDAC_QUALITY_LOW:
+      ldac_config.qualityIndex = LdacQualityIndex::QUALITY_LOW;
+      break;
+    case A2DP_LDAC_QUALITY_ABR_OFFLOAD:
+      ldac_config.qualityIndex = LdacQualityIndex::QUALITY_ABR;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown LDAC QualityIndex="
+                 << a2dp_offload.codec_info[6];
+      return false;
+  }
+  ldac_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  if (ldac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
+    LOG(ERROR) << __func__ << ": Unknown LDAC bits_per_sample="
+               << current_codec.bits_per_sample;
+    return false;
+  }
+  codec_config->config.ldacConfig(ldac_config);
+  return true;
+}
+
+bool UpdateOffloadingCapabilities(
+    const std::vector<btav_a2dp_codec_config_t>& framework_preference) {
+  audio_hal_capabilities = BluetoothAudioClientInterface::GetAudioCapabilities(
+      SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  uint32_t codec_type_masks = static_cast<uint32_t>(CodecType::UNKNOWN);
+  for (auto preference : framework_preference) {
+    switch (preference.codec_type) {
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
+        codec_type_masks |= CodecType::SBC;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+        codec_type_masks |= CodecType::AAC;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
+        codec_type_masks |= CodecType::APTX;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+        codec_type_masks |= CodecType::APTX_HD;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC:
+        codec_type_masks |= CodecType::LDAC;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
+        [[fallthrough]];
+      case BTAV_A2DP_CODEC_INDEX_SINK_AAC:
+        [[fallthrough]];
+      case BTAV_A2DP_CODEC_INDEX_SINK_LDAC:
+        LOG(WARNING) << __func__
+                     << ": Ignore sink codec_type=" << preference.codec_type;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_MAX:
+        [[fallthrough]];
+      default:
+        LOG(ERROR) << __func__
+                   << ": Unknown codec_type=" << preference.codec_type;
+        return false;
+    }
+  }
+  offloading_preference.clear();
+  for (auto capability : audio_hal_capabilities) {
+    if (static_cast<CodecType>(capability.codecCapabilities().codecType &
+                               codec_type_masks) != CodecType::UNKNOWN) {
+      LOG(INFO) << __func__
+                << ": enabled offloading capability=" << toString(capability);
+      offloading_preference.push_back(capability);
+    } else {
+      LOG(INFO) << __func__
+                << ": disabled offloading capability=" << toString(capability);
+    }
+  }
+  // TODO: Bluetooth SoC and runtime property
+  return true;
+}
+
+// Check whether this codec is supported by the audio HAL and is allowed to use
+// by prefernece of framework / Bluetooth SoC / runtime property.
+bool IsCodecOffloadingEnabled(const CodecConfiguration& codec_config) {
+  for (auto preference : offloading_preference) {
+    if (codec_config.codecType != preference.codecCapabilities().codecType)
+      continue;
+    auto codec_capability = preference.codecCapabilities();
+    switch (codec_capability.codecType) {
+      case CodecType::SBC: {
+        auto sbc_capability = codec_capability.capabilities.sbcCapabilities();
+        auto sbc_config = codec_config.config.sbcConfig();
+        return sbc_offloading_capability_match(sbc_capability, sbc_config);
+      }
+      case CodecType::AAC: {
+        auto aac_capability = codec_capability.capabilities.aacCapabilities();
+        auto aac_config = codec_config.config.aacConfig();
+        return aac_offloading_capability_match(aac_capability, aac_config);
+      }
+      case CodecType::APTX:
+        [[fallthrough]];
+      case CodecType::APTX_HD: {
+        auto aptx_capability = codec_capability.capabilities.aptxCapabilities();
+        auto aptx_config = codec_config.config.aptxConfig();
+        return aptx_offloading_capability_match(aptx_capability, aptx_config);
+      }
+      case CodecType::LDAC: {
+        auto ldac_capability = codec_capability.capabilities.ldacCapabilities();
+        auto ldac_config = codec_config.config.ldacConfig();
+        return ldac_offloading_capability_match(ldac_capability, ldac_config);
+      }
+      case CodecType::UNKNOWN:
+        [[fallthrough]];
+      default:
+        LOG(ERROR) << __func__ << ": Unknown codecType="
+                   << toString(codec_capability.codecType);
+        return false;
+    }
+  }
+  LOG(INFO) << __func__ << ": software codec=" << toString(codec_config);
+  return false;
+}
+
+}  // namespace codec
+}  // namespace audio
+}  // namespace bluetooth
diff --git a/audio_hal_interface/codec_status.h b/audio_hal_interface/codec_status.h
new file mode 100644
index 0000000..e0e0744
--- /dev/null
+++ b/audio_hal_interface/codec_status.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <android/hardware/bluetooth/audio/2.0/types.h>
+
+#include "a2dp_codec_api.h"
+
+namespace bluetooth {
+namespace audio {
+namespace codec {
+
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+
+extern const CodecConfiguration kInvalidCodecConfiguration;
+
+SampleRate A2dpCodecToHalSampleRate(
+    const btav_a2dp_codec_config_t& a2dp_codec_config);
+BitsPerSample A2dpCodecToHalBitsPerSample(
+    const btav_a2dp_codec_config_t& a2dp_codec_config);
+ChannelMode A2dpCodecToHalChannelMode(
+    const btav_a2dp_codec_config_t& a2dp_codec_config);
+
+bool A2dpSbcToHalConfig(CodecConfiguration* codec_config,
+                        A2dpCodecConfig* a2dp_config);
+bool A2dpAacToHalConfig(CodecConfiguration* codec_config,
+                        A2dpCodecConfig* a2dp_config);
+bool A2dpAptxToHalConfig(CodecConfiguration* codec_config,
+                         A2dpCodecConfig* a2dp_config);
+bool A2dpLdacToHalConfig(CodecConfiguration* codec_config,
+                         A2dpCodecConfig* a2dp_config);
+
+bool UpdateOffloadingCapabilities(
+    const std::vector<btav_a2dp_codec_config_t>& framework_preference);
+// Check whether this codec is supported by the audio HAL and is allowed to use
+// by prefernece of framework / Bluetooth SoC / runtime property.
+bool IsCodecOffloadingEnabled(const CodecConfiguration& codec_config);
+
+}  // namespace codec
+}  // namespace audio
+}  // namespace bluetooth
diff --git a/audio_hearing_aid_hw/src/audio_hearing_aid_hw.cc b/audio_hearing_aid_hw/src/audio_hearing_aid_hw.cc
index cac7302..58a019f 100644
--- a/audio_hearing_aid_hw/src/audio_hearing_aid_hw.cc
+++ b/audio_hearing_aid_hw/src/audio_hearing_aid_hw.cc
@@ -57,12 +57,11 @@
 // sockets
 #define WRITE_POLL_MS 20
 
-#define FNLOG() LOG_VERBOSE(LOG_TAG, "%s", __func__);
-#define DEBUG(fmt, ...) \
-  LOG_VERBOSE(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
-#define INFO(fmt, ...) LOG_INFO(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
-#define WARN(fmt, ...) LOG_WARN(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
-#define ERROR(fmt, ...) LOG_ERROR(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
+#define FNLOG() LOG_VERBOSE("%s", __func__);
+#define DEBUG(fmt, ...) LOG_VERBOSE("%s: " fmt, __func__, ##__VA_ARGS__)
+#define INFO(fmt, ...) LOG_INFO("%s: " fmt, __func__, ##__VA_ARGS__)
+#define WARN(fmt, ...) LOG_WARN("%s: " fmt, __func__, ##__VA_ARGS__)
+#define ERROR(fmt, ...) LOG_ERROR("%s: " fmt, __func__, ##__VA_ARGS__)
 
 #define ASSERTC(cond, msg, val)                                           \
   if (!(cond)) {                                                          \
diff --git a/binder/android/bluetooth/IBluetooth.aidl b/binder/android/bluetooth/IBluetooth.aidl
index 6d4c3f8..0e554b7 100644
--- a/binder/android/bluetooth/IBluetooth.aidl
+++ b/binder/android/bluetooth/IBluetooth.aidl
@@ -58,7 +58,7 @@
     int getDiscoverableTimeout();
     boolean setDiscoverableTimeout(int timeout);
 
-    boolean startDiscovery(String callingPackage);
+    boolean startDiscovery(String callingPackage, String callingFeatureId);
     boolean cancelDiscovery();
     boolean isDiscovering();
     long getDiscoveryEndMillis();
@@ -144,4 +144,8 @@
     boolean disconnectAllEnabledProfiles(in BluetoothDevice device);
 
     boolean setActiveDevice(in BluetoothDevice device, in int profiles);
+
+    List<BluetoothDevice> getMostRecentlyConnectedDevices();
+
+    boolean removeActiveDevice(in int profiles);
 }
diff --git a/binder/android/bluetooth/IBluetoothA2dp.aidl b/binder/android/bluetooth/IBluetoothA2dp.aidl
index 3b406cc..39157e5 100644
--- a/binder/android/bluetooth/IBluetoothA2dp.aidl
+++ b/binder/android/bluetooth/IBluetoothA2dp.aidl
@@ -52,4 +52,5 @@
     int supportsOptionalCodecs(in BluetoothDevice device);
     int getOptionalCodecsEnabled(in BluetoothDevice device);
     oneway void setOptionalCodecsEnabled(in BluetoothDevice device, int value);
+    int getPriority(in BluetoothDevice device);
 }
diff --git a/binder/android/bluetooth/IBluetoothGatt.aidl b/binder/android/bluetooth/IBluetoothGatt.aidl
index 016eeff..78d0261 100644
--- a/binder/android/bluetooth/IBluetoothGatt.aidl
+++ b/binder/android/bluetooth/IBluetoothGatt.aidl
@@ -46,9 +46,9 @@
     void registerScanner(in IScannerCallback callback, in WorkSource workSource);
     void unregisterScanner(in int scannerId);
     void startScan(in int scannerId, in ScanSettings settings, in List<ScanFilter> filters,
-                   in List scanStorages, in String callingPackage);
+                   in List scanStorages, in String callingPackage, String callingFeatureId);
     void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List<ScanFilter> filters,
-                            in String callingPackage);
+                            in String callingPackage, String callingFeatureId);
     void stopScanForIntent(in PendingIntent intent, in String callingPackage);
     void stopScan(in int scannerId);
     void flushPendingBatchResults(in int scannerId);
diff --git a/binder/android/bluetooth/IBluetoothHeadset.aidl b/binder/android/bluetooth/IBluetoothHeadset.aidl
index bc8fa05..0edac14 100644
--- a/binder/android/bluetooth/IBluetoothHeadset.aidl
+++ b/binder/android/bluetooth/IBluetoothHeadset.aidl
@@ -63,4 +63,6 @@
     boolean setActiveDevice(in BluetoothDevice device);
     BluetoothDevice getActiveDevice();
     boolean isInbandRingingEnabled();
+    boolean setPriority(in BluetoothDevice device, int connectionPolicy);
+    int getPriority(in BluetoothDevice device);
 }
diff --git a/binder/android/bluetooth/IBluetoothHearingAid.aidl b/binder/android/bluetooth/IBluetoothHearingAid.aidl
index 9ea9dae..b6e02c1 100644
--- a/binder/android/bluetooth/IBluetoothHearingAid.aidl
+++ b/binder/android/bluetooth/IBluetoothHearingAid.aidl
@@ -34,9 +34,7 @@
     List<BluetoothDevice> getActiveDevices();
     boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
     int getConnectionPolicy(in BluetoothDevice device);
-    void adjustVolume(int direction);
     void setVolume(int volume);
-    int getVolume();
 
     const int HI_SYNC_ID_INVALID = 0;
     long getHiSyncId(in BluetoothDevice device);
diff --git a/binder/android/bluetooth/IBluetoothHidDevice.aidl b/binder/android/bluetooth/IBluetoothHidDevice.aidl
index 5246262..cefb324 100644
--- a/binder/android/bluetooth/IBluetoothHidDevice.aidl
+++ b/binder/android/bluetooth/IBluetoothHidDevice.aidl
@@ -37,4 +37,5 @@
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
     String getUserAppName();
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
 }
diff --git a/binder/android/bluetooth/IBluetoothManager.aidl b/binder/android/bluetooth/IBluetoothManager.aidl
index faf78d8..8e2537d 100644
--- a/binder/android/bluetooth/IBluetoothManager.aidl
+++ b/binder/android/bluetooth/IBluetoothManager.aidl
@@ -49,8 +49,11 @@
     String getAddress();
     String getName();
 
+    boolean onFactoryReset();
+
     boolean isBleScanAlwaysAvailable();
-    int updateBleAppCount(IBinder b, boolean enable, String packageName);
+    boolean enableBle(String packageName, IBinder b);
+    boolean disableBle(String packageName, IBinder b);
     boolean isBleAppPresent();
     boolean isHearingAidProfileSupported();
 }
diff --git a/binder/android/bluetooth/IBluetoothPan.aidl b/binder/android/bluetooth/IBluetoothPan.aidl
index 4052aa4..ba27acf 100644
--- a/binder/android/bluetooth/IBluetoothPan.aidl
+++ b/binder/android/bluetooth/IBluetoothPan.aidl
@@ -26,10 +26,11 @@
 interface IBluetoothPan {
     // Public API
     boolean isTetheringOn();
-    void setBluetoothTethering(boolean value, String pkgName);
+    void setBluetoothTethering(boolean value, String pkgName, @nullable String attributionTag);
     boolean connect(in BluetoothDevice device);
     boolean disconnect(in BluetoothDevice device);
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
 }
diff --git a/binder/android/bluetooth/IBluetoothPbap.aidl b/binder/android/bluetooth/IBluetoothPbap.aidl
index 52caf77..3cb6a6f 100644
--- a/binder/android/bluetooth/IBluetoothPbap.aidl
+++ b/binder/android/bluetooth/IBluetoothPbap.aidl
@@ -28,4 +28,5 @@
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
     void disconnect(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
 }
diff --git a/bta/Android.bp b/bta/Android.bp
old mode 100644
new mode 100755
index 824076e..ac6b733
--- a/bta/Android.bp
+++ b/bta/Android.bp
@@ -142,3 +142,33 @@
         "libbt-common",
     ],
 }
+
+// bta hf client add record tests for target
+// ========================================================
+cc_test {
+    name: "net_test_hf_client_add_record",
+    defaults: ["fluoride_defaults"],
+    test_suites: ["device-tests"],
+    include_dirs: [
+        "system/bt",
+        "system/bt/bta/include",
+        "system/bt/bta/sys",
+        "system/bt/btif/include",
+        "system/bt/internal_include",
+        "system/bt/stack/include",
+        "system/bt/utils/include",
+    ],
+    srcs: [
+        "test/bta_hf_client_add_record_test.cc",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libosi",
+    ],
+    cflags: ["-DBUILDCFG"],
+}
diff --git a/bta/ag/bta_ag_cmd.cc b/bta/ag/bta_ag_cmd.cc
index 87b2a82..214ffd7 100644
--- a/bta/ag/bta_ag_cmd.cc
+++ b/bta/ag/bta_ag_cmd.cc
@@ -210,8 +210,7 @@
                                const char* p_arg, int16_t int_arg) {
   const tBTA_AG_RESULT* result = bta_ag_result_by_code(code);
   if (result == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s Unable to lookup result for code %zu", __func__,
-              code);
+    LOG_ERROR("%s Unable to lookup result for code %zu", __func__, code);
     return;
   }
 
diff --git a/bta/ar/bta_ar.cc b/bta/ar/bta_ar.cc
index bcfa89d..8e02aa4 100644
--- a/bta/ar/bta_ar.cc
+++ b/bta/ar/bta_ar.cc
@@ -102,17 +102,17 @@
   } else if (sys_id == BTA_ID_AVK) {
     bta_ar_cb.p_avk_conn_cback = p_cback;
     mask = BTA_AR_AVK_MASK;
+  } else {
+    APPL_TRACE_ERROR("%s: the registration is from wrong sys_id:%d", __func__,
+                     sys_id);
   }
-#if (BTA_AR_DEBUG == TRUE)
-  else {
-    APPL_TRACE_ERROR(
-        "bta_ar_reg_avdt: the registration is from wrong sys_id:%d", sys_id);
-  }
-#endif
 
   if (mask) {
     if (bta_ar_cb.avdt_registered == 0) {
       AVDT_Register(p_reg, bta_ar_avdt_cback);
+    } else {
+      APPL_TRACE_WARNING("%s: sys_id:%d doesn't register again (registered:%d)",
+                         __func__, sys_id, bta_ar_cb.avdt_registered);
     }
     bta_ar_cb.avdt_registered |= mask;
   }
diff --git a/bta/ar/bta_ar_int.h b/bta/ar/bta_ar_int.h
index ece8378..320fee9 100644
--- a/bta/ar/bta_ar_int.h
+++ b/bta/ar/bta_ar_int.h
@@ -27,10 +27,6 @@
 
 #include "bta_av_api.h"
 
-#ifndef BTA_AR_DEBUG
-#define BTA_AR_DEBUG TRUE
-#endif
-
 #define BTA_AR_AV_MASK 0x01
 #define BTA_AR_AVK_MASK 0x02
 
diff --git a/bta/av/bta_av_aact.cc b/bta/av/bta_av_aact.cc
index bb88777..2ebdb5b 100644
--- a/bta/av/bta_av_aact.cc
+++ b/bta/av/bta_av_aact.cc
@@ -269,7 +269,7 @@
                    bd_addr.ToString().c_str(), p_scb->recfg_sup,
                    p_scb->suspend_sup);
   if (p_scb->PeerAddress() != bd_addr) {
-    LOG_INFO(LOG_TAG, "%s: reset flags old_addr=%s new_addr=%s", __func__,
+    LOG_INFO("%s: reset flags old_addr=%s new_addr=%s", __func__,
              p_scb->PeerAddress().ToString().c_str(),
              bd_addr.ToString().c_str());
     /* a new addr, reset the supported flags */
@@ -293,9 +293,9 @@
  *
  ******************************************************************************/
 static void notify_start_failed(tBTA_AV_SCB* p_scb) {
-  LOG_ERROR(LOG_TAG, "%s: peer %s role:0x%x bta_channel:%d bta_handle:0x%x",
-            __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->role,
-            p_scb->chnl, p_scb->hndl);
+  LOG_ERROR("%s: peer %s role:0x%x bta_channel:%d bta_handle:0x%x", __func__,
+            p_scb->PeerAddress().ToString().c_str(), p_scb->role, p_scb->chnl,
+            p_scb->hndl);
   tBTA_AV_START start;
   /* if start failed, clear role */
   p_scb->role &= ~BTA_AV_ROLE_START_INT;
@@ -524,8 +524,8 @@
     return;
   }
   p_pkt->event = BTA_AV_SINK_MEDIA_DATA_EVT;
-  p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(BTA_AV_SINK_MEDIA_DATA_EVT,
-                                                    (tBTA_AV_MEDIA*)p_pkt);
+  p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(
+      p_scb->PeerAddress(), BTA_AV_SINK_MEDIA_DATA_EVT, (tBTA_AV_MEDIA*)p_pkt);
   /* Free the buffer: a copy of the packet has been delivered */
   osi_free(p_pkt);
 }
@@ -902,8 +902,7 @@
   tBTA_AV_CONN_CHG msg;
   uint8_t role = BTA_AV_ROLE_AD_INT;
 
-  LOG_INFO(LOG_TAG, "%s peer %s", __func__,
-           p_scb->PeerAddress().ToString().c_str());
+  LOG_INFO("%s peer %s", __func__, p_scb->PeerAddress().ToString().c_str());
 
   /* free any buffers */
   p_scb->sdp_discovery_started = false;
@@ -922,6 +921,7 @@
   p_scb->cur_psc_mask = 0;
   p_scb->wait = 0;
   p_scb->num_disc_snks = 0;
+  p_scb->coll_mask = 0;
   alarm_cancel(p_scb->avrc_ct_timer);
 
   /* TODO(eisenbach): RE-IMPLEMENT USING VSC OR HAL EXTENSION
@@ -1061,18 +1061,21 @@
                            UNUSED_ATTR tBTA_AV_DATA* p_data) {
   tBTA_AV_RCB* p_rcb;
 
-  APPL_TRACE_WARNING("%s: conn_lcb: 0x%x peer_addr: %s", __func__,
-                     bta_av_cb.conn_lcb,
-                     p_scb->PeerAddress().ToString().c_str());
+  APPL_TRACE_API("%s: conn_lcb: 0x%x peer_addr: %s", __func__,
+                 bta_av_cb.conn_lcb, p_scb->PeerAddress().ToString().c_str());
 
   alarm_cancel(bta_av_cb.link_signalling_timer);
   alarm_cancel(p_scb->avrc_ct_timer);
 
-  if (bta_av_cb.conn_lcb) {
+  // conn_lcb is the index bitmask of all used LCBs, and since LCB and SCB use
+  // the same index, it should be safe to use SCB index here.
+  if ((bta_av_cb.conn_lcb & (1 << p_scb->hdi)) != 0) {
     p_rcb = bta_av_get_rcb_by_shdl((uint8_t)(p_scb->hdi + 1));
     if (p_rcb) bta_av_del_rc(p_rcb);
     AVDT_DisconnectReq(p_scb->PeerAddress(), &bta_av_proc_stream_evt);
   } else {
+    APPL_TRACE_WARNING("%s: conn_lcb=0x%x bta_handle=0x%x (hdi=%u) no link",
+                       __func__, bta_av_cb.conn_lcb, p_scb->hndl, p_scb->hdi);
     bta_av_ssm_execute(p_scb, BTA_AV_AVDT_DISCONNECT_EVT, NULL);
   }
 }
@@ -1134,7 +1137,6 @@
   local_sep = bta_av_get_scb_sep_type(p_scb, avdt_handle);
   bta_av_adjust_seps_idx(p_scb, avdt_handle);
   LOG_DEBUG(
-      LOG_TAG,
       "%s: peer %s bta_handle=0x%x avdt_handle=%d sep_idx=%d cur_psc_mask:0x%x",
       __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
       p_scb->avdt_handle, p_scb->sep_idx, p_scb->cur_psc_mask);
@@ -1145,8 +1147,8 @@
     tBTA_AV_MEDIA av_sink_codec_info;
     av_sink_codec_info.avk_config.bd_addr = p_scb->PeerAddress();
     av_sink_codec_info.avk_config.codec_info = p_scb->cfg.codec_info;
-    p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(BTA_AV_SINK_MEDIA_CFG_EVT,
-                                                      &av_sink_codec_info);
+    p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(
+        p_scb->PeerAddress(), BTA_AV_SINK_MEDIA_CFG_EVT, &av_sink_codec_info);
   }
 
   AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label,
@@ -1398,15 +1400,16 @@
  *
  ******************************************************************************/
 void bta_av_connect_req(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
-  APPL_TRACE_DEBUG("%s: peer %s coll_mask:0x%x", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s coll_mask=0x%02x", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->coll_mask);
   p_scb->sdp_discovery_started = false;
   if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) {
     /* SNK initiated L2C connection while SRC was doing SDP.    */
     /* Wait until timeout to check if SNK starts signalling.    */
-    APPL_TRACE_EVENT("%s: coll_mask = 0x%2X", __func__, p_scb->coll_mask);
+    APPL_TRACE_WARNING("%s: coll_mask=0x%02x incoming timer is up", __func__,
+                       p_scb->coll_mask);
     p_scb->coll_mask |= BTA_AV_COLL_API_CALLED;
-    APPL_TRACE_EVENT("%s: updated coll_mask = 0x%2X", __func__,
+    APPL_TRACE_EVENT("%s: updated coll_mask=0x%02x", __func__,
                      p_scb->coll_mask);
     return;
   }
@@ -1746,8 +1749,8 @@
     else if (uuid_int == UUID_SERVCLASS_AUDIO_SINK)
       bta_av_adjust_seps_idx(p_scb,
                              bta_av_get_scb_handle(p_scb, AVDT_TSEP_SNK));
-    LOG_DEBUG(LOG_TAG, "%s: sep_idx=%d avdt_handle=%d bta_handle=0x%x",
-              __func__, p_scb->sep_idx, p_scb->avdt_handle, p_scb->hndl);
+    LOG_DEBUG("%s: sep_idx=%d avdt_handle=%d bta_handle=0x%x", __func__,
+              p_scb->sep_idx, p_scb->avdt_handle, p_scb->hndl);
 
     /* use only the services peer supports */
     cfg.psc_mask &= p_scb->peer_cap.psc_mask;
@@ -1765,7 +1768,7 @@
       av_sink_codec_info.avk_config.bd_addr = p_scb->PeerAddress();
       av_sink_codec_info.avk_config.codec_info = p_scb->cfg.codec_info;
       p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(
-          BTA_AV_SINK_MEDIA_CFG_EVT, &av_sink_codec_info);
+          p_scb->PeerAddress(), BTA_AV_SINK_MEDIA_CFG_EVT, &av_sink_codec_info);
     }
 
     if (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE) {
@@ -1796,7 +1799,7 @@
   uint8_t avdt_handle = p_data->ci_setconfig.avdt_handle;
 
   bta_av_adjust_seps_idx(p_scb, avdt_handle);
-  LOG_DEBUG(LOG_TAG, "%s: sep_idx=%d avdt_handle=%d bta_handle=0x%x", __func__,
+  LOG_DEBUG("%s: sep_idx=%d avdt_handle=%d bta_handle=0x%x", __func__,
             p_scb->sep_idx, p_scb->avdt_handle, p_scb->hndl);
   AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_UNSUP_CFG, 0);
 
@@ -1854,8 +1857,7 @@
   uint8_t clear_policy = 0;
   uint8_t cur_role;
 
-  LOG_INFO(LOG_TAG,
-           "%s: peer %s sco_occupied:%s role:0x%x started:%s wait:0x%x",
+  LOG_INFO("%s: peer %s sco_occupied:%s role:0x%x started:%s wait:0x%x",
            __func__, p_scb->PeerAddress().ToString().c_str(),
            logbool(bta_av_cb.sco_occupied).c_str(), p_scb->role,
            logbool(p_scb->started).c_str(), p_scb->wait);
@@ -1878,7 +1880,6 @@
     p_scb->role |= BTA_AV_ROLE_START_INT;
     if (p_scb->wait != 0) {
       LOG_WARN(
-          LOG_TAG,
           "%s: peer %s start stream request ignored: "
           "already waiting: sco_occupied:%s role:0x%x started:%s wait:0x%x",
           __func__, p_scb->PeerAddress().ToString().c_str(),
@@ -1896,7 +1897,6 @@
 
   if ((p_scb->role & BTA_AV_ROLE_START_INT) != 0) {
     LOG_WARN(
-        LOG_TAG,
         "%s: peer %s start stream request ignored: "
         "already initiated: sco_occupied:%s role:0x%x started:%s wait:0x%x",
         __func__, p_scb->PeerAddress().ToString().c_str(),
@@ -1909,15 +1909,16 @@
   bta_sys_busy(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->PeerAddress());
   uint16_t result = AVDT_StartReq(&p_scb->avdt_handle, 1);
   if (result != AVDT_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: AVDT_StartReq failed for peer %s result:%d",
-              __func__, p_scb->PeerAddress().ToString().c_str(), result);
+    LOG_ERROR("%s: AVDT_StartReq failed for peer %s result:%d", __func__,
+              p_scb->PeerAddress().ToString().c_str(), result);
+    bta_av_start_failed(p_scb, p_data);
   }
-  LOG_INFO(LOG_TAG,
-           "%s: peer %s start requested: sco_occupied:%s role:0x%x "
-           "started:%s wait:0x%x",
-           __func__, p_scb->PeerAddress().ToString().c_str(),
-           logbool(bta_av_cb.sco_occupied).c_str(), p_scb->role,
-           logbool(p_scb->started).c_str(), p_scb->wait);
+  LOG_INFO(
+      "%s: peer %s start requested: sco_occupied:%s role:0x%x "
+      "started:%s wait:0x%x",
+      __func__, p_scb->PeerAddress().ToString().c_str(),
+      logbool(bta_av_cb.sco_occupied).c_str(), p_scb->role,
+      logbool(p_scb->started).c_str(), p_scb->wait);
 }
 
 /*******************************************************************************
@@ -2247,8 +2248,7 @@
   uint8_t cur_role;
   uint8_t local_tsep = p_scb->seps[p_scb->sep_idx].tsep;
 
-  LOG_INFO(LOG_TAG,
-           "%s: peer %s bta_handle:0x%x wait:0x%x role:0x%x local_tsep:%d",
+  LOG_INFO("%s: peer %s bta_handle:0x%x wait:0x%x role:0x%x local_tsep:%d",
            __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
            p_scb->wait, p_scb->role, local_tsep);
 
@@ -2690,9 +2690,13 @@
   } else {
     /* open failed. try again */
     p_scb->num_recfg++;
-    if (bta_av_cb.conn_lcb) {
+    // conn_lcb is the index bitmask of all used LCBs, and since LCB and SCB use
+    // the same index, it should be safe to use SCB index here.
+    if ((bta_av_cb.conn_lcb & (1 << p_scb->hdi)) != 0) {
       AVDT_DisconnectReq(p_scb->PeerAddress(), &bta_av_proc_stream_evt);
     } else {
+      APPL_TRACE_WARNING("%s: conn_lcb=0x%x bta_handle=0x%x (hdi=%u) no link",
+                         __func__, bta_av_cb.conn_lcb, p_scb->hndl, p_scb->hdi);
       bta_av_connect_req(p_scb, NULL);
     }
   }
@@ -2829,11 +2833,10 @@
       if (interop_match_name(INTEROP_DISABLE_AVDTP_RECONFIGURE, remote_name) ||
           interop_match_addr(INTEROP_DISABLE_AVDTP_RECONFIGURE,
                              (const RawAddress*)&p_scb->PeerAddress())) {
-        LOG_INFO(LOG_TAG,
-                 "%s: disable AVDTP RECONFIGURE: interop matched "
-                 "name %s address %s",
-                 __func__, remote_name,
-                 p_scb->PeerAddress().ToString().c_str());
+        LOG_INFO(
+            "%s: disable AVDTP RECONFIGURE: interop matched "
+            "name %s address %s",
+            __func__, remote_name, p_scb->PeerAddress().ToString().c_str());
         disable_avdtp_reconfigure = true;
       }
     }
@@ -2893,8 +2896,8 @@
     /* we may choose to use a different SEP at reconfig.
      * adjust the sep_idx now */
     bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SRC));
-    LOG_DEBUG(LOG_TAG, "%s: sep_idx=%d avdt_handle=%d bta_handle=0x%x",
-              __func__, p_scb->sep_idx, p_scb->avdt_handle, p_scb->hndl);
+    LOG_DEBUG("%s: sep_idx=%d avdt_handle=%d bta_handle=0x%x", __func__,
+              p_scb->sep_idx, p_scb->avdt_handle, p_scb->hndl);
 
     /* open the stream with the new config */
     p_scb->sep_info_idx = p_scb->rcfg_idx;
@@ -2930,11 +2933,11 @@
  ******************************************************************************/
 void bta_av_chk_2nd_start(tBTA_AV_SCB* p_scb,
                           UNUSED_ATTR tBTA_AV_DATA* p_data) {
-  LOG_INFO(LOG_TAG,
-           "%s: peer %s channel:%d bta_av_cb.audio_open_cnt:%d role:0x%x "
-           "features:0x%x",
-           __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->chnl,
-           bta_av_cb.audio_open_cnt, p_scb->role, bta_av_cb.features);
+  LOG_INFO(
+      "%s: peer %s channel:%d bta_av_cb.audio_open_cnt:%d role:0x%x "
+      "features:0x%x",
+      __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->chnl,
+      bta_av_cb.audio_open_cnt, p_scb->role, bta_av_cb.features);
 
   if ((p_scb->chnl == BTA_AV_CHNL_AUDIO) && (bta_av_cb.audio_open_cnt >= 2) &&
       (((p_scb->role & BTA_AV_ROLE_AD_ACP) == 0) ||  // Outgoing connection or
@@ -2953,11 +2956,11 @@
           if (!new_started) {
             // Start the new stream
             new_started = true;
-            LOG_INFO(LOG_TAG,
-                     "%s: starting new stream for peer %s because peer %s "
-                     "already started",
-                     __func__, p_scb->PeerAddress().ToString().c_str(),
-                     p_scbi->PeerAddress().ToString().c_str());
+            LOG_INFO(
+                "%s: starting new stream for peer %s because peer %s "
+                "already started",
+                __func__, p_scb->PeerAddress().ToString().c_str(),
+                p_scbi->PeerAddress().ToString().c_str());
             bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL);
           }
           // May need to update the flush timeout of this already started stream
@@ -3057,7 +3060,7 @@
 void bta_av_open_at_inc(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   memcpy(&(p_scb->open_api), &(p_data->api_open), sizeof(tBTA_AV_API_OPEN));
 
-  APPL_TRACE_DEBUG("%s: peer %s coll_mask:0x%x", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s coll_mask=0x%02x", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->coll_mask);
 
   if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) {
@@ -3294,8 +3297,7 @@
       p_a2dp_offload->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_96000;
       break;
   }
-  if (L2CA_GetIdentifiers(p_scb->l2c_cid, &p_a2dp_offload->l2c_rcid, NULL) ==
-      false) {
+  if (L2CA_GetRemoteCid(p_scb->l2c_cid, &p_a2dp_offload->l2c_rcid) == false) {
     APPL_TRACE_ERROR("%s: Failed to fetch l2c rcid", __func__);
     return;
   }
diff --git a/bta/av/bta_av_act.cc b/bta/av/bta_av_act.cc
index a727842..5ef5cf0 100644
--- a/bta/av/bta_av_act.cc
+++ b/bta/av/bta_av_act.cc
@@ -362,6 +362,7 @@
   p_rcb->shdl = shdl;
   p_rcb->lidx = lidx;
   p_rcb->peer_features = 0;
+  p_rcb->cover_art_psm = 0;
   if (lidx == (BTA_AV_NUM_LINKS + 1)) {
     /* this LIDX is reserved for the AVRCP ACP connection */
     p_cb->rc_acp_handle = p_rcb->handle;
@@ -500,7 +501,7 @@
       APPL_TRACE_DEBUG("%s: shdl:%d, srch %d", __func__, i + 1,
                        p_scb->rc_handle);
       shdl = i + 1;
-      LOG_INFO(LOG_TAG, "%s: allow incoming AVRCP connections:%d", __func__,
+      LOG_INFO("%s: allow incoming AVRCP connections:%d", __func__,
                p_scb->use_rc);
       alarm_cancel(p_scb->avrc_ct_timer);
       disc = p_scb->hndl;
@@ -560,9 +561,11 @@
 
   rc_open.peer_addr = p_data->rc_conn_chg.peer_addr;
   rc_open.peer_features = p_cb->rcb[i].peer_features;
+  rc_open.cover_art_psm = p_cb->rcb[i].cover_art_psm;
   rc_open.status = BTA_AV_SUCCESS;
   APPL_TRACE_DEBUG("%s: local features:x%x peer_features:x%x", __func__,
                    p_cb->features, rc_open.peer_features);
+  APPL_TRACE_DEBUG("%s: cover art psm:x%x", __func__, rc_open.cover_art_psm);
   if (rc_open.peer_features == 0) {
     /* we have not done SDP on peer RC capabilities.
      * peer must have initiated the RC connection */
@@ -1170,8 +1173,8 @@
             "p_lcb_rc->conn_msk:x%x",
             __func__, p_lcb_rc->conn_msk);
         /* check if the RC is connected to the scb addr */
-        LOG_INFO(LOG_TAG, "%s: p_lcb_rc->addr: %s conn_chg.peer_addr: %s",
-                 __func__, p_lcb_rc->addr.ToString().c_str(),
+        LOG_INFO("%s: p_lcb_rc->addr: %s conn_chg.peer_addr: %s", __func__,
+                 p_lcb_rc->addr.ToString().c_str(),
                  p_data->conn_chg.peer_addr.ToString().c_str());
 
         if (p_lcb_rc->conn_msk &&
@@ -1301,6 +1304,7 @@
  ******************************************************************************/
 void bta_av_disable(tBTA_AV_CB* p_cb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
   BT_HDR hdr;
+  bool disabling_in_progress = false;
   uint16_t xx;
 
   p_cb->disabling = true;
@@ -1315,8 +1319,13 @@
     if (p_cb->p_scb[xx] != NULL) {
       hdr.layer_specific = xx + 1;
       bta_av_api_deregister((tBTA_AV_DATA*)&hdr);
+      disabling_in_progress = true;
     }
   }
+  // Since All channels are deregistering by API_DEREGISTER, the DEREG_COMP_EVT
+  // would come first before API_DISABLE if there is no connections, and it is
+  // no needed to setup this disabling flag.
+  p_cb->disabling = disabling_in_progress;
 
   alarm_free(p_cb->link_signalling_timer);
   p_cb->link_signalling_timer = NULL;
@@ -1423,8 +1432,7 @@
         AVDT_DisconnectReq(p_data->str_msg.bd_addr, NULL);
         return;
       }
-      LOG_INFO(LOG_TAG,
-               "%s: AVDT_CONNECT_IND_EVT: peer %s selected lcb_index %d",
+      LOG_INFO("%s: AVDT_CONNECT_IND_EVT: peer %s selected lcb_index %d",
                __func__, p_data->str_msg.bd_addr.ToString().c_str(), xx);
 
       tBTA_AV_SCB* p_scb = p_cb->p_scb[xx];
@@ -1474,7 +1482,7 @@
 #endif
   else {
     /* disconnected. */
-    APPL_TRACE_DEBUG("%s: bta_av_cb.conn_lcb is %d", __func__,
+    APPL_TRACE_DEBUG("%s: bta_av_cb.conn_lcb=0x%x", __func__,
                      bta_av_cb.conn_lcb);
 
     p_lcb = bta_av_find_lcb(p_data->str_msg.bd_addr, BTA_AV_LCB_FREE);
@@ -1500,7 +1508,8 @@
       }
     }
   }
-  APPL_TRACE_DEBUG("%s: sig_chg conn_lcb: 0x%x", __func__, p_cb->conn_lcb);
+  APPL_TRACE_DEBUG("%s: bta_av_cb.conn_lcb=0x%x after sig_chg", __func__,
+                   p_cb->conn_lcb);
 }
 
 /*******************************************************************************
@@ -1567,7 +1576,7 @@
     p_scb = p_cb->p_scb[inx];
   }
   if (p_scb) {
-    APPL_TRACE_DEBUG("%s: coll_mask = 0x%02X", __func__, p_scb->coll_mask);
+    APPL_TRACE_DEBUG("%s: coll_mask=0x%02x", __func__, p_scb->coll_mask);
 
     if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) {
       p_scb->coll_mask &= ~BTA_AV_COLL_INC_TMR;
@@ -1719,26 +1728,32 @@
       if (peer_rc_version >= AVRC_REV_1_3)
         peer_features |= (BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_METADATA);
 
-      /*
-       * Though Absolute Volume came after in 1.4 and above, but there are few
-       * devices
-       * in market which supports absolute Volume and they are still 1.3
-       * TO avoid IOT issuses with those devices, we check for 1.3 as minimum
-       * version
-       */
-      if (peer_rc_version >= AVRC_REV_1_3) {
-        /* get supported features */
-        tSDP_DISC_ATTR* p_attr =
-            SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES);
-        if (p_attr != NULL) {
-          uint16_t categories = p_attr->attr_value.v.u16;
-          if (categories & AVRC_SUPF_CT_CAT2)
+      /* Get supported features */
+      tSDP_DISC_ATTR* p_attr =
+          SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES);
+      if (p_attr != NULL) {
+        uint16_t categories = p_attr->attr_value.v.u16;
+        /*
+         * Though Absolute Volume came after in 1.4 and above, but there are
+         * few devices in market which supports absolute Volume and they are
+         * still 1.3. To avoid IOP issuses with those devices, we check for
+         * 1.3 as minimum version
+         */
+        if (peer_rc_version >= AVRC_REV_1_3) {
+          if (categories & AVRC_SUPF_TG_CAT2)
             peer_features |= (BTA_AV_FEAT_ADV_CTRL);
-          if (categories & AVRC_SUPF_CT_APP_SETTINGS)
+          if (categories & AVRC_SUPF_TG_APP_SETTINGS)
             peer_features |= (BTA_AV_FEAT_APP_SETTING);
-          if (categories & AVRC_SUPF_CT_BROWSE)
+          if (categories & AVRC_SUPF_TG_BROWSE)
             peer_features |= (BTA_AV_FEAT_BROWSE);
         }
+
+        /* AVRCP Cover Artwork over BIP */
+        if (peer_rc_version >= AVRC_REV_1_6) {
+          if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET &&
+              categories & AVRC_SUPF_TG_PLAYER_COVER_ART)
+            peer_features |= (BTA_AV_FEAT_COVER_ARTWORK);
+        }
       }
     }
     /* get next record; if none found, we're done */
@@ -1748,6 +1763,108 @@
   return peer_features;
 }
 
+/******************************************************************************
+ *
+ * Function         bta_avk_get_cover_art_psm
+ *
+ * Description      Get the PSM associated with the AVRCP Target cover art
+ *                  feature
+ *
+ * Returns          uint16_t PSM value used to get cover artwork, or 0x0000 if
+ *                  one does not exist.
+ *
+ *****************************************************************************/
+uint16_t bta_avk_get_cover_art_psm() {
+  APPL_TRACE_DEBUG("%s: searching for cover art psm", __func__);
+  /* Cover Art L2CAP PSM is only available on a target device */
+  tBTA_AV_CB* p_cb = &bta_av_cb;
+  tSDP_DISC_REC* p_rec =
+      SDP_FindServiceInDb(p_cb->p_disc_db, UUID_SERVCLASS_AV_REM_CTRL_TARGET,
+          NULL);
+  while (p_rec) {
+    tSDP_DISC_ATTR* p_attr =
+        (SDP_FindAttributeInRec(p_rec, ATTR_ID_ADDITION_PROTO_DESC_LISTS));
+    /*
+     * If we have the Additional Protocol Description Lists attribute then we
+     * specifically want the list that is an L2CAP protocol leading to OBEX.
+     * Because the is a case where cover art is supported and browsing isn't
+     * we need to check each list for the one we want.
+     *
+     * This means we need to do drop down into the protocol list and do a
+     * "for each protocol, for each protocol element, for each protocol element
+     * list parameter, if the parameter is L2CAP then find the PSM associated
+     * with it, then make sure we see OBEX in that same protocol"
+     */
+    if (p_attr != NULL && SDP_DISC_ATTR_TYPE(p_attr->attr_len_type)
+        == DATA_ELE_SEQ_DESC_TYPE) {
+      // Point to first in List of protocols (i.e [(L2CAP -> AVCTP),
+      // (L2CAP -> OBEX)])
+      tSDP_DISC_ATTR* p_protocol_list = p_attr->attr_value.v.p_sub_attr;
+      while (p_protocol_list != NULL) {
+        if (SDP_DISC_ATTR_TYPE(p_protocol_list->attr_len_type)
+            == DATA_ELE_SEQ_DESC_TYPE) {
+          // Point to fist in list of protocol elements (i.e. [L2CAP, AVCTP])
+          tSDP_DISC_ATTR* p_protocol =
+              p_protocol_list->attr_value.v.p_sub_attr;
+          bool protocol_has_obex = false;
+          bool protocol_has_l2cap = false;
+          uint16_t psm = 0x0000;
+          while (p_protocol) {
+            if (SDP_DISC_ATTR_TYPE(p_protocol->attr_len_type)
+                == DATA_ELE_SEQ_DESC_TYPE) {
+              // Point to first item protocol parameters list (i.e [UUID=L2CAP,
+              // PSM=0x1234])
+              tSDP_DISC_ATTR* p_protocol_param =
+                  p_protocol->attr_value.v.p_sub_attr;
+              /*
+               * Currently there's only ever one UUID and one parameter. L2cap
+               * has a single PSM, AVCTP has a version and OBEX has nothing.
+               * Change this if that ever changes.
+               */
+              uint16_t protocol_uuid = 0;
+              uint16_t protocol_param = 0;
+              while (p_protocol_param) {
+                uint16_t param_type =
+                    SDP_DISC_ATTR_TYPE(p_protocol_param->attr_len_type);
+                uint16_t param_len =
+                    SDP_DISC_ATTR_LEN(p_protocol_param->attr_len_type);
+                if (param_type == UUID_DESC_TYPE) {
+                  protocol_uuid = p_protocol_param->attr_value.v.u16;
+                } else if (param_type == UINT_DESC_TYPE) {
+                    protocol_param = (param_len == 2)
+                      ? p_protocol_param->attr_value.v.u16
+                      : p_protocol_param->attr_value.v.u8;
+                } /* else dont care */
+                p_protocol_param = p_protocol_param->p_next_attr;  // next
+              }
+              // If we've found L2CAP then the parameter is a PSM
+              if (protocol_uuid == UUID_PROTOCOL_L2CAP) {
+                protocol_has_l2cap = true;
+                psm = protocol_param;
+              } else if (protocol_uuid == UUID_PROTOCOL_OBEX) {
+                protocol_has_obex = true;
+              }
+            }
+            // If this protocol has l2cap and obex then we're found the BIP PSM
+            if (protocol_has_l2cap && protocol_has_obex) {
+              APPL_TRACE_DEBUG("%s: found psm 0x%x", __func__, psm);
+              return psm;
+            }
+            p_protocol = p_protocol->p_next_attr;  // next protocol element
+          }
+        }
+        p_protocol_list = p_protocol_list->p_next_attr;  // next protocol
+      }
+    }
+    /* get next record; if none found, we're done */
+    p_rec = SDP_FindServiceInDb(p_cb->p_disc_db,
+        UUID_SERVCLASS_AV_REM_CTRL_TARGET, p_rec);
+  }
+  /* L2CAP PSM range is 0x1000-0xFFFF so 0x0000 is safe default invalid */
+  APPL_TRACE_DEBUG("%s: could not find a BIP psm", __func__);
+  return 0x0000;
+}
+
 /*******************************************************************************
  *
  * Function         bta_av_rc_disc_done
@@ -1764,6 +1881,7 @@
   tBTA_AV_LCB* p_lcb;
   uint8_t rc_handle;
   tBTA_AV_FEAT peer_features = 0; /* peer features mask */
+  uint16_t cover_art_psm = 0x0000;
 
   APPL_TRACE_DEBUG("%s: bta_av_rc_disc_done disc:x%x", __func__, p_cb->disc);
   if (!p_cb->disc) {
@@ -1797,6 +1915,12 @@
     if (BTA_AV_FEAT_ADV_CTRL &
         bta_avk_check_peer_features(UUID_SERVCLASS_AV_REMOTE_CONTROL))
       peer_features |= (BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_RCCT);
+
+    if (peer_features & BTA_AV_FEAT_COVER_ARTWORK)
+      cover_art_psm = bta_avk_get_cover_art_psm();
+
+    APPL_TRACE_DEBUG("%s: populating rem ctrl target bip psm 0x%x", __func__,
+                     cover_art_psm);
   } else
 #endif
       if (p_cb->sdp_a2dp_handle) {
@@ -1849,6 +1973,7 @@
           rc_handle = bta_av_rc_create(p_cb, AVCT_INT,
                                        (uint8_t)(p_scb->hdi + 1), p_lcb->lidx);
           p_cb->rcb[rc_handle].peer_features = peer_features;
+          p_cb->rcb[rc_handle].cover_art_psm = cover_art_psm;
         } else {
           APPL_TRACE_ERROR("%s: can not find LCB!!", __func__);
         }
@@ -1858,6 +1983,7 @@
         tBTA_AV_RC_OPEN rc_open;
         rc_open.peer_addr = p_scb->PeerAddress();
         rc_open.peer_features = 0;
+        rc_open.cover_art_psm = 0;
         rc_open.status = BTA_AV_FAIL_SDP;
         tBTA_AV bta_av_data;
         bta_av_data.rc_open = rc_open;
@@ -1879,9 +2005,28 @@
     } else {
       rc_feat.peer_addr = p_scb->PeerAddress();
     }
-    tBTA_AV bta_av_data;
-    bta_av_data.rc_feat = rc_feat;
-    (*p_cb->p_cback)(BTA_AV_RC_FEAT_EVT, &bta_av_data);
+
+    tBTA_AV bta_av_feat;
+    bta_av_feat.rc_feat = rc_feat;
+    (*p_cb->p_cback)(BTA_AV_RC_FEAT_EVT, &bta_av_feat);
+
+    // Send PSM data
+    APPL_TRACE_DEBUG("%s: Send PSM data", __func__);
+    tBTA_AV_RC_PSM rc_psm;
+    p_cb->rcb[rc_handle].cover_art_psm = cover_art_psm;
+    rc_psm.rc_handle = rc_handle;
+    rc_psm.cover_art_psm = cover_art_psm;
+    if (p_scb == NULL) {
+      rc_psm.peer_addr = p_cb->lcb[p_cb->rcb[rc_handle].lidx - 1].addr;
+    } else {
+      rc_psm.peer_addr = p_scb->PeerAddress();
+    }
+
+    APPL_TRACE_DEBUG("%s: rc_psm = 0x%x", __func__, rc_psm.cover_art_psm);
+
+    tBTA_AV bta_av_psm;
+    bta_av_psm.rc_cover_art_psm = rc_psm;
+    (*p_cb->p_cback)(BTA_AV_RC_PSM_EVT, &bta_av_psm);
   }
 }
 
@@ -1915,6 +2060,7 @@
       rc_close.rc_handle = i;
       p_rcb->status &= ~BTA_AV_RC_CONN_MASK;
       p_rcb->peer_features = 0;
+      p_rcb->cover_art_psm = 0;
       APPL_TRACE_DEBUG("%s: shdl:%d, lidx:%d", __func__, p_rcb->shdl,
                        p_rcb->lidx);
       if (p_rcb->shdl) {
@@ -1933,7 +2079,7 @@
         /* if the RCB uses the extra LCB, use the addr for event and clean it */
         p_lcb = &p_cb->lcb[BTA_AV_NUM_LINKS];
         rc_close.peer_addr = p_msg->peer_addr;
-        LOG_INFO(LOG_TAG, "%s: rc_only closed bd_addr: %s", __func__,
+        LOG_INFO("%s: rc_only closed bd_addr: %s", __func__,
                  p_msg->peer_addr.ToString().c_str());
         p_lcb->conn_msk = 0;
         p_lcb->lidx = 0;
@@ -1986,7 +2132,7 @@
   tBTA_AV_RC_CONN_CHG* p_msg = (tBTA_AV_RC_CONN_CHG*)p_data;
   tBTA_AV_RC_BROWSE_OPEN rc_browse_open;
 
-  LOG_INFO(LOG_TAG, "%s: peer_addr: %s rc_handle:%d", __func__,
+  LOG_INFO("%s: peer_addr: %s rc_handle:%d", __func__,
            p_msg->peer_addr.ToString().c_str(), p_msg->handle);
 
   rc_browse_open.status = BTA_AV_SUCCESS;
@@ -2012,7 +2158,7 @@
   tBTA_AV_RC_CONN_CHG* p_msg = (tBTA_AV_RC_CONN_CHG*)p_data;
   tBTA_AV_RC_BROWSE_CLOSE rc_browse_close;
 
-  LOG_INFO(LOG_TAG, "%s: peer_addr: %s rc_handle:%d", __func__,
+  LOG_INFO("%s: peer_addr: %s rc_handle:%d", __func__,
            p_msg->peer_addr.ToString().c_str(), p_msg->handle);
 
   rc_browse_close.rc_handle = p_msg->handle;
@@ -2037,7 +2183,8 @@
   tAVRC_SDP_DB_PARAMS db_params;
   uint16_t attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST,
                           ATTR_ID_BT_PROFILE_DESC_LIST,
-                          ATTR_ID_SUPPORTED_FEATURES};
+                          ATTR_ID_SUPPORTED_FEATURES,
+                          ATTR_ID_ADDITION_PROTO_DESC_LISTS};
   uint8_t hdi;
   tBTA_AV_SCB* p_scb;
   RawAddress peer_addr = RawAddress::kEmpty;
@@ -2070,7 +2217,7 @@
 
     /* set up parameters */
     db_params.db_len = BTA_AV_DISC_BUF_SIZE;
-    db_params.num_attr = 3;
+    db_params.num_attr = sizeof(attr_list) / sizeof(uint16_t);
     db_params.p_db = p_cb->p_disc_db;
     db_params.p_attrs = attr_list;
 
@@ -2108,9 +2255,9 @@
                      p_scb->hndl);
     mask = BTA_AV_HNDL_TO_MSK(p_scb->hdi);
     p_cb->reg_audio &= ~mask;
-    if ((p_cb->conn_audio & mask) && bta_av_cb.audio_open_cnt) {
+    if ((p_cb->conn_audio & mask) && p_cb->audio_open_cnt) {
       /* this channel is still marked as open. decrease the count */
-      bta_av_cb.audio_open_cnt--;
+      p_cb->audio_open_cnt--;
     }
     p_cb->conn_audio &= ~mask;
 
@@ -2161,7 +2308,9 @@
 
     if (p_cb->disabling) {
       p_cb->disabling = false;
-      bta_av_cb.features = 0;
+      // reset enabling parameters
+      p_cb->features = 0;
+      p_cb->sec_mask = 0;
     }
 
     /* Clear the Capturing service class bit */
diff --git a/bta/av/bta_av_api.cc b/bta/av/bta_av_api.cc
index aed5b12..b27cc52 100644
--- a/bta/av/bta_av_api.cc
+++ b/bta/av/bta_av_api.cc
@@ -156,8 +156,7 @@
  ******************************************************************************/
 void BTA_AvOpen(const RawAddress& bd_addr, tBTA_AV_HNDL handle, bool use_rc,
                 tBTA_SEC sec_mask, uint16_t uuid) {
-  LOG_INFO(LOG_TAG,
-           "%s: peer %s bta_handle:0x%x use_rc=%s sec_mask=0x%x uuid=0x%x",
+  LOG_INFO("%s: peer %s bta_handle:0x%x use_rc=%s sec_mask=0x%x uuid=0x%x",
            __func__, bd_addr.ToString().c_str(), handle,
            (use_rc) ? "true" : "false", sec_mask, uuid);
 
@@ -185,7 +184,7 @@
  *
  ******************************************************************************/
 void BTA_AvClose(tBTA_AV_HNDL handle) {
-  LOG_INFO(LOG_TAG, "%s: bta_handle:0x%x", __func__, handle);
+  LOG_INFO("%s: bta_handle:0x%x", __func__, handle);
 
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
@@ -205,7 +204,7 @@
  *
  ******************************************************************************/
 void BTA_AvDisconnect(const RawAddress& bd_addr) {
-  LOG_INFO(LOG_TAG, "%s: peer %s", __func__, bd_addr.ToString().c_str());
+  LOG_INFO("%s: peer %s", __func__, bd_addr.ToString().c_str());
 
   tBTA_AV_API_DISCNT* p_buf =
       (tBTA_AV_API_DISCNT*)osi_malloc(sizeof(tBTA_AV_API_DISCNT));
@@ -226,7 +225,7 @@
  *
  ******************************************************************************/
 void BTA_AvStart(tBTA_AV_HNDL handle) {
-  LOG_INFO(LOG_TAG, "%s: bta_handle=0x%x", __func__, handle);
+  LOG_INFO("%s: bta_handle=0x%x", __func__, handle);
 
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
@@ -246,7 +245,7 @@
  *
  ******************************************************************************/
 void BTA_AvOffloadStart(tBTA_AV_HNDL hndl) {
-  LOG_INFO(LOG_TAG, "%s: bta_handle=0x%x", __func__, hndl);
+  LOG_INFO("%s: bta_handle=0x%x", __func__, hndl);
 
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
@@ -288,7 +287,7 @@
  *
  ******************************************************************************/
 void BTA_AvStop(tBTA_AV_HNDL handle, bool suspend) {
-  LOG_INFO(LOG_TAG, "%s: bta_handle=0x%x suspend=%s", __func__, handle,
+  LOG_INFO("%s: bta_handle=0x%x suspend=%s", __func__, handle,
            logbool(suspend).c_str());
 
   tBTA_AV_API_STOP* p_buf =
@@ -319,8 +318,8 @@
 void BTA_AvReconfig(tBTA_AV_HNDL hndl, bool suspend, uint8_t sep_info_idx,
                     uint8_t* p_codec_info, uint8_t num_protect,
                     const uint8_t* p_protect_info) {
-  LOG_INFO(LOG_TAG, "%s: bta_handle=0x%x suspend=%s sep_info_idx=%d", __func__,
-           hndl, logbool(suspend).c_str(), sep_info_idx);
+  LOG_INFO("%s: bta_handle=0x%x suspend=%s sep_info_idx=%d", __func__, hndl,
+           logbool(suspend).c_str(), sep_info_idx);
 
   tBTA_AV_API_RCFG* p_buf =
       (tBTA_AV_API_RCFG*)osi_malloc(sizeof(tBTA_AV_API_RCFG) + num_protect);
diff --git a/bta/av/bta_av_cfg.cc b/bta/av/bta_av_cfg.cc
index 1da38b3..8583496 100644
--- a/bta/av/bta_av_cfg.cc
+++ b/bta/av/bta_av_cfg.cc
@@ -42,7 +42,10 @@
 
 /* AVRCP supported categories */
 #define BTA_AV_RC_SUPF_CT (AVRC_SUPF_CT_CAT2)
-#define BTA_AVK_RC_SUPF_CT (AVRC_SUPF_CT_CAT1 | AVRC_SUPF_CT_BROWSE)
+#define BTA_AVK_RC_SUPF_CT (AVRC_SUPF_CT_CAT1 |                     \
+                            AVRC_SUPF_CT_BROWSE |                   \
+                            AVRC_SUPF_CT_COVER_ART_GET_IMAGE_PROP | \
+                            AVRC_SUPF_CT_COVER_ART_GET_IMAGE)
 #define BTA_AVK_RC_SUPF_TG (AVRC_SUPF_TG_CAT2)
 
 /* AVRCP Controller and Targer default name */
diff --git a/bta/av/bta_av_ci.cc b/bta/av/bta_av_ci.cc
index e92c8d6..ed6523a 100644
--- a/bta/av/bta_av_ci.cc
+++ b/bta/av/bta_av_ci.cc
@@ -70,11 +70,11 @@
 void bta_av_ci_setconfig(tBTA_AV_HNDL bta_av_handle, uint8_t err_code,
                          uint8_t category, uint8_t num_seid, uint8_t* p_seid,
                          bool recfg_needed, uint8_t avdt_handle) {
-  LOG_DEBUG(LOG_TAG,
-            "%s: bta_av_handle=0x%x err_code=%d category=%d "
-            "num_seid=%d recfg_needed=%s avdt_handle=%d",
-            __func__, bta_av_handle, err_code, category, num_seid,
-            recfg_needed ? "true" : "false", avdt_handle);
+  LOG_DEBUG(
+      "%s: bta_av_handle=0x%x err_code=%d category=%d "
+      "num_seid=%d recfg_needed=%s avdt_handle=%d",
+      __func__, bta_av_handle, err_code, category, num_seid,
+      recfg_needed ? "true" : "false", avdt_handle);
 
   tBTA_AV_CI_SETCONFIG* p_buf =
       (tBTA_AV_CI_SETCONFIG*)osi_malloc(sizeof(tBTA_AV_CI_SETCONFIG));
diff --git a/bta/av/bta_av_int.h b/bta/av/bta_av_int.h
index a4479932..3925e55 100644
--- a/bta/av/bta_av_int.h
+++ b/bta/av/bta_av_int.h
@@ -571,6 +571,7 @@
   uint8_t shdl;               /* stream handle (hdi + 1) */
   uint8_t lidx;               /* (index+1) to LCB */
   tBTA_AV_FEAT peer_features; /* peer features mask */
+  uint16_t cover_art_psm;     /* BIP PSM for cover art feature */
 } tBTA_AV_RCB;
 #define BTA_AV_NUM_RCB (BTA_AV_NUM_STRS + 2)
 
@@ -602,6 +603,7 @@
   tBTA_SEC sec_mask;            /* security mask */
   tBTA_AV_HNDL handle;          /* the handle for SDP activity */
   bool disabling;               /* true if api disabled called */
+  uint8_t enabling_attempts;    // counter to wait for previous disabling
   uint8_t
       disc; /* (hdi+1) or (rc_handle|BTA_AV_CHNL_MSK) if p_disc_db is in use */
   uint8_t state;          /* state machine state */
@@ -616,6 +618,10 @@
   uint8_t audio_streams; /* handle mask of streaming audio channels */
 } tBTA_AV_CB;
 
+// total attempts are half seconds
+constexpr uint32_t kEnablingAttemptsIntervalMs = 100;
+constexpr uint8_t kEnablingAttemptsCountMaximum = 5;
+
 // A2DP offload VSC parameters
 class tBT_A2DP_OFFLOAD {
  public:
diff --git a/bta/av/bta_av_main.cc b/bta/av/bta_av_main.cc
index 7d31fcf..8b8129b 100644
--- a/bta/av/bta_av_main.cc
+++ b/bta/av/bta_av_main.cc
@@ -203,7 +203,7 @@
  ****************************************************************************/
 
 /* AV control block */
-tBTA_AV_CB bta_av_cb;
+tBTA_AV_CB bta_av_cb = {};
 
 static const char* bta_av_st_code(uint8_t state);
 
@@ -218,6 +218,39 @@
  *
  ******************************************************************************/
 static void bta_av_api_enable(tBTA_AV_DATA* p_data) {
+  if (bta_av_cb.disabling) {
+    APPL_TRACE_WARNING(
+        "%s: previous (reg_audio=%#x) is still disabling (attempts=%d)",
+        __func__, bta_av_cb.reg_audio, bta_av_cb.enabling_attempts);
+    if (++bta_av_cb.enabling_attempts <= kEnablingAttemptsCountMaximum) {
+      tBTA_AV_API_ENABLE* p_buf =
+          (tBTA_AV_API_ENABLE*)osi_malloc(sizeof(tBTA_AV_API_ENABLE));
+      memcpy(p_buf, &p_data->api_enable, sizeof(tBTA_AV_API_ENABLE));
+      bta_sys_sendmsg_delayed(p_buf, base::TimeDelta::FromMilliseconds(
+                                         kEnablingAttemptsIntervalMs));
+      return;
+    }
+    if (bta_av_cb.sdp_a2dp_handle) {
+      SDP_DeleteRecord(bta_av_cb.sdp_a2dp_handle);
+      bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SOURCE);
+    }
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+    if (bta_av_cb.sdp_a2dp_snk_handle) {
+      SDP_DeleteRecord(bta_av_cb.sdp_a2dp_snk_handle);
+      bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SINK);
+    }
+#endif
+#if (BTA_AR_INCLUDED == TRUE)
+    // deregister from AVDT
+    bta_ar_dereg_avdt(BTA_ID_AV);
+
+    // deregister from AVCT
+    bta_ar_dereg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, BTA_ID_AV);
+    bta_ar_dereg_avrc(UUID_SERVCLASS_AV_REM_CTRL_TARGET, BTA_ID_AV);
+    bta_ar_dereg_avct(BTA_ID_AV);
+#endif
+  }
+
   /* initialize control block */
   memset(&bta_av_cb, 0, sizeof(tBTA_AV_CB));
 
@@ -369,7 +402,7 @@
   peer_address_ = peer_address;
 
   if (peer_address.IsEmpty()) {
-    LOG_ERROR(LOG_TAG, "%s: Invalid peer address: %s", __func__,
+    LOG_ERROR("%s: Invalid peer address: %s", __func__,
               peer_address.ToString().c_str());
     return;
   }
@@ -379,8 +412,8 @@
   size_t version_value_size = sizeof(avdtp_version);
   if (!btif_config_get_bin(peer_address_.ToString(), AVDTP_VERSION_CONFIG_KEY,
                            (uint8_t*)&avdtp_version, &version_value_size)) {
-    LOG_WARN(LOG_TAG, "%s: Failed to read cached peer AVDTP version for %s",
-             __func__, peer_address_.ToString().c_str());
+    LOG_WARN("%s: Failed to read cached peer AVDTP version for %s", __func__,
+             peer_address_.ToString().c_str());
   } else {
     SetAvdtpVersion(avdtp_version);
   }
@@ -393,7 +426,7 @@
 
 void tBTA_AV_SCB::SetAvdtpVersion(uint16_t avdtp_version) {
   avdtp_version_ = avdtp_version;
-  LOG_DEBUG(LOG_TAG, "%s: AVDTP version for %s set to 0x%x", __func__,
+  LOG_DEBUG("%s: AVDTP version for %s set to 0x%x", __func__,
             peer_address_.ToString().c_str(), avdtp_version_);
 }
 
@@ -430,7 +463,7 @@
       APPL_TRACE_DEBUG("%s: bta_handle x%x, role x%x", __func__, p_scb->hndl,
                        p_scb->role);
     }
-    LOG_INFO(LOG_TAG, "%s: conn_cback bd_addr: %s", __func__,
+    LOG_INFO("%s: conn_cback bd_addr: %s", __func__,
              bd_addr.ToString().c_str());
     bta_sys_sendmsg(p_msg);
   }
@@ -471,6 +504,21 @@
   char* p_service_name;
   tBTA_UTL_COD cod;
 
+  if (bta_av_cb.disabling ||
+      (bta_av_cb.features == 0 && bta_av_cb.sec_mask == 0)) {
+    APPL_TRACE_WARNING(
+        "%s: AV instance (features=%#x, sec_mask=%#x, reg_audio=%#x) is not "
+        "ready for app_id %d",
+        __func__, bta_av_cb.features, bta_av_cb.sec_mask, bta_av_cb.reg_audio,
+        p_data->api_reg.app_id);
+    tBTA_AV_API_REG* p_buf =
+        (tBTA_AV_API_REG*)osi_malloc(sizeof(tBTA_AV_API_REG));
+    memcpy(p_buf, &p_data->api_reg, sizeof(tBTA_AV_API_REG));
+    bta_sys_sendmsg_delayed(
+        p_buf, base::TimeDelta::FromMilliseconds(kEnablingAttemptsIntervalMs));
+    return;
+  }
+
   avdtp_stream_config.Reset();
 
   registr.status = BTA_AV_FAIL_RESOURCES;
@@ -480,8 +528,7 @@
   char avrcp_version[PROPERTY_VALUE_MAX] = {0};
   osi_property_get(AVRCP_VERSION_PROPERTY, avrcp_version,
                    AVRCP_DEFAULT_VERSION);
-  LOG_INFO(LOG_TAG, "%s: AVRCP version used for sdp: \"%s\"", __func__,
-           avrcp_version);
+  LOG_INFO("%s: AVRCP version used for sdp: \"%s\"", __func__, avrcp_version);
 
   uint16_t profile_initialized = p_data->api_reg.service_uuid;
   if (profile_initialized == UUID_SERVCLASS_AUDIO_SINK) {
@@ -490,7 +537,7 @@
     p_bta_av_cfg = &bta_av_cfg;
 
     if (!strncmp(AVRCP_1_3_STRING, avrcp_version, sizeof(AVRCP_1_3_STRING))) {
-      LOG_INFO(LOG_TAG, "%s: AVRCP 1.3 capabilites used", __func__);
+      LOG_INFO("%s: AVRCP 1.3 capabilites used", __func__);
       p_bta_av_cfg = &bta_av_cfg_compatibility;
     }
   }
@@ -641,6 +688,9 @@
       }
       if (AVDT_CreateStream(p_scb->app_id, &p_scb->seps[codec_index].av_handle,
                             avdtp_stream_config) != AVDT_SUCCESS) {
+        APPL_TRACE_WARNING(
+            "%s: bta_handle=0x%x (app_id %d) failed to alloc an SEP index:%d",
+            __func__, p_scb->hndl, p_scb->app_id, codec_index);
         continue;
       }
       /* Save a copy of the codec */
@@ -841,12 +891,12 @@
     }
   }
 
-  LOG_INFO(LOG_TAG,
-           "%s: peer %s channel:%d bta_av_cb.audio_open_cnt:%d role:0x%x "
-           "features:0x%x start:%s",
-           __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->chnl,
-           bta_av_cb.audio_open_cnt, p_scb->role, bta_av_cb.features,
-           logbool(start).c_str());
+  LOG_INFO(
+      "%s: peer %s channel:%d bta_av_cb.audio_open_cnt:%d role:0x%x "
+      "features:0x%x start:%s",
+      __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->chnl,
+      bta_av_cb.audio_open_cnt, p_scb->role, bta_av_cb.features,
+      logbool(start).c_str());
   return start;
 }
 
@@ -1094,11 +1144,11 @@
   bool is_ok = true;
 
   if (BTM_GetRole(p_scb->PeerAddress(), &role) == BTM_SUCCESS) {
-    LOG_INFO(LOG_TAG,
-             "%s: peer %s bta_handle:0x%x role:%d conn_audio:0x%x bits:%d "
-             "features:0x%x",
-             __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
-             role, bta_av_cb.conn_audio, bits, bta_av_cb.features);
+    LOG_INFO(
+        "%s: peer %s bta_handle:0x%x role:%d conn_audio:0x%x bits:%d "
+        "features:0x%x",
+        __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl, role,
+        bta_av_cb.conn_audio, bits, bta_av_cb.features);
     if (BTM_ROLE_MASTER != role &&
         (A2DP_BitsSet(bta_av_cb.conn_audio) > bits ||
          (bta_av_cb.features & BTA_AV_FEAT_MASTER))) {
@@ -1110,8 +1160,7 @@
           BTM_SwitchRole(p_scb->PeerAddress(), BTM_ROLE_MASTER, NULL);
       if (status != BTM_CMD_STARTED) {
         /* can not switch role on SCB - start the timer on SCB */
-        LOG_ERROR(LOG_TAG,
-                  "%s: peer %s BTM_SwitchRole(BTM_ROLE_MASTER) error: %d",
+        LOG_ERROR("%s: peer %s BTM_SwitchRole(BTM_ROLE_MASTER) error: %d",
                   __func__, p_scb->PeerAddress().ToString().c_str(), status);
       }
       if (status != BTM_MODE_UNSUPPORTED && status != BTM_DEV_BLACKLISTED) {
diff --git a/bta/dm/bta_dm_act.cc b/bta/dm/bta_dm_act.cc
index 4942eea..d6342f8 100644
--- a/bta/dm/bta_dm_act.cc
+++ b/bta/dm/bta_dm_act.cc
@@ -813,15 +813,13 @@
 }
 
 /** Bonds with peer device */
-void bta_dm_bond(const RawAddress& bd_addr, tBTA_TRANSPORT transport) {
-  tBTM_STATUS status;
+void bta_dm_bond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                 tBTA_TRANSPORT transport, int device_type) {
   tBTA_DM_SEC sec_event;
   char* p_name;
 
-  if (transport == BTA_TRANSPORT_UNKNOWN)
-    status = BTM_SecBond(bd_addr, 0, NULL, 0);
-  else
-    status = BTM_SecBondByTransport(bd_addr, transport, 0, NULL, 0);
+  tBTM_STATUS status =
+      BTM_SecBond(bd_addr, addr_type, transport, device_type, 0, NULL, 0);
 
   if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED)) {
     memset(&sec_event, 0, sizeof(tBTA_DM_SEC));
@@ -1756,7 +1754,9 @@
   if (bta_dm_search_cb.p_search_cback) {
     bta_dm_search_cb.p_search_cback(BTA_DM_SEARCH_CANCEL_CMPL_EVT, NULL);
   }
-  if (!bta_dm_search_cb.name_discover_done) {
+  if (!bta_dm_search_cb.name_discover_done &&
+      (bta_dm_search_cb.state == BTA_DM_SEARCH_ACTIVE ||
+       bta_dm_search_cb.state == BTA_DM_SEARCH_CANCELLING)) {
     BTM_CancelRemoteDeviceName();
   }
   if (bta_dm_search_cb.gatt_disc_active) {
@@ -1785,7 +1785,7 @@
                        bta_dm_search_cb.services);
       /* try to search all services by search based on L2CAP UUID */
       if (bta_dm_search_cb.services == BTA_ALL_SERVICE_MASK) {
-        LOG_INFO(LOG_TAG, "%s services_to_search=%08x", __func__,
+        LOG_INFO("%s services_to_search=%08x", __func__,
                  bta_dm_search_cb.services_to_search);
         if (bta_dm_search_cb.services_to_search & BTA_RES_SERVICE_MASK) {
           uuid = Uuid::From16Bit(bta_service_id_to_uuid_lkup_tbl[0]);
@@ -1829,8 +1829,7 @@
         uuid = bta_dm_search_cb.uuid;
       }
 
-      LOG_INFO(LOG_TAG, "%s search UUID = %s", __func__,
-               uuid.ToString().c_str());
+      LOG_INFO("%s search UUID = %s", __func__, uuid.ToString().c_str());
       SDP_InitDiscoveryDb(bta_dm_search_cb.p_sdp_db, BTA_DM_SDP_DB_SIZE, 1,
                           &uuid, 0, NULL);
 
@@ -2674,8 +2673,7 @@
 
   tBTA_DM_PEER_DEVICE* p_dev = bta_dm_find_peer_device(bd_addr);
   if (!p_dev) return;
-  LOG_INFO(LOG_TAG,
-           "%s: peer %s info:0x%x new_role:0x%x dev count:%d hci_status=%d",
+  LOG_INFO("%s: peer %s info:0x%x new_role:0x%x dev count:%d hci_status=%d",
            __func__, bd_addr.ToString().c_str(), p_dev->info, new_role,
            bta_dm_cb.device_list.count, hci_status);
   if (p_dev->info & BTA_DM_DI_AV_ACTIVE) {
@@ -4190,7 +4188,7 @@
         __func__, bta_dm_search_cb.ble_raw_size, bta_dm_search_cb.ble_raw_used);
   }
 
-  LOG_INFO(LOG_TAG, "%s service_id_uuid_len=%zu", __func__,
+  LOG_INFO("%s service_id_uuid_len=%zu", __func__,
            service_id.uuid.GetShortestRepresentationSize());
   if (bta_dm_search_cb.state != BTA_DM_SEARCH_IDLE) {
     /* send result back to app now, one by one */
diff --git a/bta/dm/bta_dm_api.cc b/bta/dm/bta_dm_api.cc
index 979d2f6..f0f83dd 100644
--- a/bta/dm/bta_dm_api.cc
+++ b/bta/dm/bta_dm_api.cc
@@ -210,15 +210,10 @@
 }
 
 /** This function initiates a bonding procedure with a peer device */
-void BTA_DmBond(const RawAddress& bd_addr) {
-  do_in_main_thread(FROM_HERE,
-                    base::Bind(bta_dm_bond, bd_addr, BTA_TRANSPORT_UNKNOWN));
-}
-
-/** This function initiates a bonding procedure with a peer device */
-void BTA_DmBondByTransport(const RawAddress& bd_addr,
-                           tBTA_TRANSPORT transport) {
-  do_in_main_thread(FROM_HERE, base::Bind(bta_dm_bond, bd_addr, transport));
+void BTA_DmBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                tBTA_TRANSPORT transport, int device_type) {
+  do_in_main_thread(FROM_HERE, base::Bind(bta_dm_bond, bd_addr, addr_type,
+                                          transport, device_type));
 }
 
 /** This function cancels the bonding procedure with a peer device
@@ -551,24 +546,6 @@
 
 /*******************************************************************************
  *
- * Function         BTA_DmSetBleConnScanParams
- *
- * Description      This function is called to set scan parameters used in
- *                  BLE connection request
- *
- * Parameters:      scan_interval    - scan interval
- *                  scan_window      - scan window
- *
- * Returns          void
- *
- ******************************************************************************/
-void BTA_DmSetBleConnScanParams(uint32_t scan_interval, uint32_t scan_window) {
-  do_in_main_thread(FROM_HERE, base::Bind(bta_dm_ble_set_conn_scan_params,
-                                          scan_interval, scan_window));
-}
-
-/*******************************************************************************
- *
  * Function         bta_dm_discover_send_msg
  *
  * Description      This function send discover message to BTA task.
@@ -631,77 +608,6 @@
 
 /*******************************************************************************
  *
- * Function         BTA_DmDiscoverExt
- *
- * Description      This function does service discovery for services of a
- *                  peer device. When services.num_uuid is 0, it indicates all
- *                  GATT based services are to be searched; other wise a list of
- *                  UUID of interested services should be provided through
- *                  p_services->p_uuid.
- *
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-void BTA_DmDiscoverExt(const RawAddress& bd_addr,
-                       tBTA_SERVICE_MASK_EXT* p_services,
-                       tBTA_DM_SEARCH_CBACK* p_cback, bool sdp_search) {
-  bta_dm_discover_send_msg(bd_addr, p_services, p_cback, sdp_search,
-                           BTA_TRANSPORT_UNKNOWN);
-}
-
-/*******************************************************************************
- *
- * Function         BTA_DmSearchExt
- *
- * Description      This function searches for peer Bluetooth devices. It
- *                  performs an inquiry and gets the remote name for devices.
- *                  Service discovery is done if services is non zero
- *
- * Parameters       p_dm_inq: inquiry conditions
- *                  p_services: if service is not empty, service discovery will
- *                              be done. For all GATT based service conditions,
- *                              put num_uuid, and p_uuid is the pointer to the
- *                              list of UUID values.
- *                  p_cback: callback function when search is completed.
- *
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-void BTA_DmSearchExt(tBTA_DM_INQ* p_dm_inq, tBTA_SERVICE_MASK_EXT* p_services,
-                     tBTA_DM_SEARCH_CBACK* p_cback) {
-  const size_t len =
-      p_services
-          ? (sizeof(tBTA_DM_API_SEARCH) + sizeof(Uuid) * p_services->num_uuid)
-          : sizeof(tBTA_DM_API_SEARCH);
-  tBTA_DM_API_SEARCH* p_msg = (tBTA_DM_API_SEARCH*)osi_calloc(len);
-
-  p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
-  memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));
-  p_msg->p_cback = p_cback;
-  p_msg->rs_res = BTA_DM_RS_NONE;
-
-  if (p_services != NULL) {
-    p_msg->services = p_services->srvc_mask;
-    p_msg->num_uuid = p_services->num_uuid;
-
-    if (p_services->num_uuid != 0) {
-      p_msg->p_uuid = (Uuid*)(p_msg + 1);
-      memcpy(p_msg->p_uuid, p_services->p_uuid,
-             sizeof(Uuid) * p_services->num_uuid);
-    } else {
-      p_msg->p_uuid = NULL;
-    }
-  }
-
-  bta_sys_sendmsg(p_msg);
-}
-
-/*******************************************************************************
- *
  * Function         BTA_DmBleUpdateConnectionParam
  *
  * Description      Update connection parameters, can only be used when
diff --git a/bta/dm/bta_dm_int.h b/bta/dm/bta_dm_int.h
index 97d7f80..8bc86f4 100644
--- a/bta/dm/bta_dm_int.h
+++ b/bta/dm/bta_dm_int.h
@@ -480,7 +480,7 @@
 extern void bta_dm_set_visibility(tBTA_DM_DISC, tBTA_DM_CONN, uint8_t, uint8_t);
 extern void bta_dm_set_scan_config(tBTA_DM_MSG* p_data);
 extern void bta_dm_vendor_spec_command(tBTA_DM_MSG* p_data);
-extern void bta_dm_bond(const RawAddress&, tBTA_TRANSPORT);
+extern void bta_dm_bond(const RawAddress&, tBLE_ADDR_TYPE, tBTA_TRANSPORT, int);
 extern void bta_dm_bond_cancel(const RawAddress&);
 extern void bta_dm_pin_reply(std::unique_ptr<tBTA_DM_API_PIN_REPLY> msg);
 extern void bta_dm_add_device(std::unique_ptr<tBTA_DM_API_ADD_DEVICE> msg);
diff --git a/bta/gatt/bta_gattc_cache.cc b/bta/gatt/bta_gattc_cache.cc
index eed1d67..af91e74 100644
--- a/bta/gatt/bta_gattc_cache.cc
+++ b/bta/gatt/bta_gattc_cache.cc
@@ -676,7 +676,7 @@
                            int* count) {
   tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
 
-  LOG_DEBUG(LOG_TAG, "%s", __func__);
+  LOG_DEBUG("%s", __func__);
   if (p_clcb == NULL) {
     LOG(ERROR) << "Unknown conn_id=" << loghex(conn_id);
     return;
diff --git a/bta/hearing_aid/hearing_aid.cc b/bta/hearing_aid/hearing_aid.cc
index 75116f4..5c50c1a 100644
--- a/bta/hearing_aid/hearing_aid.cc
+++ b/bta/hearing_aid/hearing_aid.cc
@@ -921,9 +921,6 @@
     }
 
     if (hearingDevice->first_connection) {
-      /* add device into BG connection to accept remote initiated connection */
-      BTA_GATTC_Open(gatt_if, address, false, GATT_TRANSPORT_LE, false);
-
       btif_storage_add_hearing_aid(*hearingDevice);
 
       hearingDevice->first_connection = false;
@@ -1507,7 +1504,10 @@
 
     DoDisconnectCleanUp(hearingDevice);
 
-    // Keep this hearing aid in the list, and allow to reconnect back.
+    // This is needed just for the first connection. After stack is restarted,
+    // code that loads device will add them to whitelist.
+    BTA_GATTC_Open(gatt_if, hearingDevice->address, false, GATT_TRANSPORT_LE,
+                   false);
 
     callbacks->OnConnectionState(ConnectionState::DISCONNECTED, remote_bda);
 
diff --git a/bta/hf_client/bta_hf_client_at.cc b/bta/hf_client/bta_hf_client_at.cc
index 6e4fe26..6aa49fe 100644
--- a/bta/hf_client/bta_hf_client_at.cc
+++ b/bta/hf_client/bta_hf_client_at.cc
@@ -143,8 +143,7 @@
 static void bta_hf_client_at_resp_timer_cback(void* data) {
   tBTA_HF_CLIENT_CB* client_cb = (tBTA_HF_CLIENT_CB*)data;
   if (client_cb->at_cb.current_cmd == BTA_HF_CLIENT_AT_CNUM) {
-    LOG_INFO(LOG_TAG,
-             "%s: timed out waiting for AT+CNUM response; spoofing OK.",
+    LOG_INFO("%s: timed out waiting for AT+CNUM response; spoofing OK.",
              __func__);
     bta_hf_client_handle_ok(client_cb);
   } else {
diff --git a/bta/hf_client/bta_hf_client_int.h b/bta/hf_client/bta_hf_client_int.h
old mode 100644
new mode 100755
index 99967f7..4cd16fa
--- a/bta/hf_client/bta_hf_client_int.h
+++ b/bta/hf_client/bta_hf_client_int.h
@@ -28,6 +28,7 @@
 #define HFP_VERSION_1_1 0x0101
 #define HFP_VERSION_1_5 0x0105
 #define HFP_VERSION_1_6 0x0106
+#define HFP_VERSION_1_7 0x0107
 
 /* RFCOMM MTU SIZE */
 #define BTA_HF_CLIENT_MTU 256
diff --git a/bta/hf_client/bta_hf_client_rfc.cc b/bta/hf_client/bta_hf_client_rfc.cc
index f3e0947..535371f 100644
--- a/bta/hf_client/bta_hf_client_rfc.cc
+++ b/bta/hf_client/bta_hf_client_rfc.cc
@@ -127,6 +127,7 @@
     } else {
       APPL_TRACE_ERROR("%s: PORT_SUCCESS, ignoring handle = %d", __func__,
                        port_handle);
+      osi_free(p_buf);
       return;
     }
   } else if (client_cb != NULL &&
@@ -136,6 +137,10 @@
 
     RFCOMM_RemoveServer(port_handle);
     p_buf->hdr.event = BTA_HF_CLIENT_RFC_CLOSE_EVT;
+  } else if (client_cb == NULL) {
+    // client_cb is already cleaned due to hfp client disabled.
+    // Assigned a valid event value to header and send this message anyway.
+    p_buf->hdr.event = BTA_HF_CLIENT_RFC_CLOSE_EVT;
   }
 
   p_buf->hdr.layer_specific = client_cb != NULL ? client_cb->handle : 0;
diff --git a/bta/hf_client/bta_hf_client_sco.cc b/bta/hf_client/bta_hf_client_sco.cc
index faf970d..956b6d6 100644
--- a/bta/hf_client/bta_hf_client_sco.cc
+++ b/bta/hf_client/bta_hf_client_sco.cc
@@ -305,8 +305,7 @@
         case BTA_HF_CLIENT_SCO_LISTEN_E:
           /* create SCO listen connection */
           bta_hf_client_sco_create(client_cb, false);
-          /* TODO(b/143901894): Is this correct? */
-          [[fallthrough]];
+          break;
 
         case BTA_HF_CLIENT_SCO_OPEN_E:
           /* remove listening connection */
diff --git a/bta/hf_client/bta_hf_client_sdp.cc b/bta/hf_client/bta_hf_client_sdp.cc
old mode 100644
new mode 100755
index d68adcc..ab4108f
--- a/bta/hf_client/bta_hf_client_sdp.cc
+++ b/bta/hf_client/bta_hf_client_sdp.cc
@@ -33,6 +33,7 @@
 #include "bta_hf_client_int.h"
 #include "bta_sys.h"
 #include "osi/include/osi.h"
+#include "osi/include/properties.h"
 
 using bluetooth::Uuid;
 
@@ -122,6 +123,9 @@
   profile_uuid = UUID_SERVCLASS_HF_HANDSFREE;
   version = HFP_VERSION_1_6;
 
+  if (osi_property_get_bool("persist.bluetooth.hfpclient.sco_s4_supported", false))
+    version = HFP_VERSION_1_7;
+
   result &= SDP_AddProfileDescriptorList(sdp_handle, profile_uuid, version);
 
   /* add service name */
diff --git a/bta/hh/bta_hh_act.cc b/bta/hh/bta_hh_act.cc
index f64fbf0..81163c7 100644
--- a/bta/hh/bta_hh_act.cc
+++ b/bta/hh/bta_hh_act.cc
@@ -451,7 +451,7 @@
      */
     if ((status == BTA_HH_ERR_SDP) && (p_cb->incoming_conn) &&
         (p_cb->app_id == 0)) {
-      APPL_TRACE_DEBUG("%s: SDP failed for  incoming conn :hndl %d", __func__,
+      APPL_TRACE_ERROR("%s: SDP failed for  incoming conn hndl: %d", __func__,
                        p_cb->incoming_hid_handle);
       HID_HostRemoveDev(p_cb->incoming_hid_handle);
     }
@@ -470,6 +470,8 @@
     bta_hh_trace_dev_db();
 #endif
   }
+  p_cb->incoming_conn = false;
+  p_cb->incoming_hid_handle = BTA_HH_INVALID_HANDLE;
   return;
 }
 
diff --git a/bta/hh/bta_hh_api.cc b/bta/hh/bta_hh_api.cc
index cd32ede..9be4d2a 100644
--- a/bta/hh/bta_hh_api.cc
+++ b/bta/hh/bta_hh_api.cc
@@ -63,7 +63,7 @@
   tBTA_HH_API_ENABLE* p_buf =
       (tBTA_HH_API_ENABLE*)osi_calloc(sizeof(tBTA_HH_API_ENABLE));
 
-  LOG_INFO(LOG_TAG, "%s sec_mask:0x%x p_cback:%p", __func__, sec_mask, p_cback);
+  LOG_INFO("%s sec_mask:0x%x p_cback:%p", __func__, sec_mask, p_cback);
 
   /* register with BTA system manager */
   bta_sys_register(BTA_ID_HH, &bta_hh_reg);
diff --git a/bta/hh/bta_hh_le.cc b/bta/hh/bta_hh_le.cc
old mode 100644
new mode 100755
index 510e3a4..1bfc8cd
--- a/bta/hh/bta_hh_le.cc
+++ b/bta/hh/bta_hh_le.cc
@@ -66,10 +66,9 @@
 
 static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
 static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB* p_cb, bool check_bond);
-// TODO(jpawlowski): uncomment when fixed
-// static void bta_hh_process_cache_rpt (tBTA_HH_DEV_CB *p_cb,
-//                                       tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache,
-//                                       uint8_t num_rpt);
+static void bta_hh_process_cache_rpt(tBTA_HH_DEV_CB* p_cb,
+                                     tBTA_HH_RPT_CACHE_ENTRY* p_rpt_cache,
+                                     uint8_t num_rpt);
 
 #if (BTA_HH_DEBUG == TRUE)
 static const char* bta_hh_le_rpt_name[4] = {"UNKNOWN", "INPUT", "OUTPUT",
@@ -463,7 +462,7 @@
       BTA_GATTC_GetCharacteristic(conn_id, char_handle);
 
   if (!p_char) {
-    LOG_WARN(LOG_TAG, "%s No such characteristic: %d", __func__, char_handle);
+    LOG_WARN("%s No such characteristic: %d", __func__, char_handle);
     return NULL;
   }
 
@@ -1001,14 +1000,12 @@
       APPL_TRACE_DEBUG("bta_hh_security_cmpl no reports loaded, try to load");
 
       /* start loading the cache if not in stack */
-      // TODO(jpawlowski): cache storage is broken, fix it
-      // tBTA_HH_RPT_CACHE_ENTRY     *p_rpt_cache;
-      // uint8_t                       num_rpt = 0;
-      // if ((p_rpt_cache = bta_hh_le_co_cache_load(p_cb->addr, &num_rpt,
-      // p_cb->app_id)) != NULL)
-      // {
-      //     bta_hh_process_cache_rpt(p_cb, p_rpt_cache, num_rpt);
-      // }
+      tBTA_HH_RPT_CACHE_ENTRY* p_rpt_cache;
+      uint8_t num_rpt = 0;
+      if ((p_rpt_cache = bta_hh_le_co_cache_load(p_cb->addr, &num_rpt,
+                                                 p_cb->app_id)) != NULL) {
+        bta_hh_process_cache_rpt(p_cb, p_rpt_cache, num_rpt);
+      }
     }
     /*  discovery has been done for HID service */
     if (p_cb->app_id != 0 && p_cb->hid_srvc.in_use) {
@@ -1404,7 +1401,7 @@
     if (!charac.uuid.Is16Bit()) continue;
 
     uint16_t uuid16 = charac.uuid.As16Bit();
-    LOG_DEBUG(LOG_TAG, "%s: %s %s", __func__, bta_hh_uuid_to_str(uuid16),
+    LOG_DEBUG("%s: %s %s", __func__, bta_hh_uuid_to_str(uuid16),
               charac.uuid.ToString().c_str());
 
     switch (uuid16) {
@@ -1576,9 +1573,11 @@
 
   app_id = p_dev_cb->app_id;
 
-  p_rpt =
-      bta_hh_le_find_report_entry(p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id,
-                                  p_char->uuid.As16Bit(), p_char->value_handle);
+  const gatt::Service* p_svc =
+      BTA_GATTC_GetOwningService(p_dev_cb->conn_id, p_char->value_handle);
+
+  p_rpt = bta_hh_le_find_report_entry(
+      p_dev_cb, p_svc->handle, p_char->uuid.As16Bit(), p_char->value_handle);
   if (p_rpt == NULL) {
     APPL_TRACE_ERROR(
         "%s: notification received for Unknown Report, uuid: %s, handle: "
@@ -2165,52 +2164,40 @@
  * Parameters:
  *
  ******************************************************************************/
-// TODO(jpawlowski): uncomment when fixed
-// static void bta_hh_process_cache_rpt (tBTA_HH_DEV_CB *p_cb,
-//                                       tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache,
-//                                       uint8_t num_rpt)
-// {
-//     uint8_t                       i = 0;
-//     tBTA_HH_LE_RPT              *p_rpt;
+static void bta_hh_process_cache_rpt(tBTA_HH_DEV_CB* p_cb,
+                                     tBTA_HH_RPT_CACHE_ENTRY* p_rpt_cache,
+                                     uint8_t num_rpt) {
+  uint8_t i = 0;
+  tBTA_HH_LE_RPT* p_rpt;
 
-//     if (num_rpt != 0)  /* no cache is found */
-//     {
-//         p_cb->hid_srvc.in_use = true;
+  if (num_rpt != 0) /* no cache is found */
+  {
+    p_cb->hid_srvc.in_use = true;
 
-//         /* set the descriptor info */
-//         p_cb->hid_srvc.descriptor.dl_len =
-//                 p_cb->dscp_info.descriptor.dl_len;
-//         p_cb->hid_srvc.descriptor.dsc_list =
-//                     p_cb->dscp_info.descriptor.dsc_list;
+    /* set the descriptor info */
+    p_cb->hid_srvc.descriptor.dl_len = p_cb->dscp_info.descriptor.dl_len;
+    p_cb->hid_srvc.descriptor.dsc_list = p_cb->dscp_info.descriptor.dsc_list;
 
-//         for (; i <num_rpt; i ++, p_rpt_cache ++)
-//         {
-//             if ((p_rpt = bta_hh_le_find_alloc_report_entry (p_cb,
-//                                                p_rpt_cache->srvc_inst_id,
-//                                                p_rpt_cache->rpt_uuid,
-//                                                p_rpt_cache->char_inst_id,
-//                                                p_rpt_cache->prop))  == NULL)
-//             {
-//                 APPL_TRACE_ERROR("bta_hh_process_cache_rpt: allocation report
-//                 entry failure");
-//                 break;
-//             }
-//             else
-//             {
-//                 p_rpt->rpt_type =  p_rpt_cache->rpt_type;
-//                 p_rpt->rpt_id   =  p_rpt_cache->rpt_id;
+    for (; i < num_rpt; i++, p_rpt_cache++) {
+      if ((p_rpt = bta_hh_le_find_alloc_report_entry(
+               p_cb, p_rpt_cache->srvc_inst_id, p_rpt_cache->rpt_uuid,
+               p_rpt_cache->char_inst_id)) == NULL) {
+        APPL_TRACE_ERROR(
+            "bta_hh_process_cache_rpt: allocation report entry failure");
+        break;
+      } else {
+        p_rpt->rpt_type = p_rpt_cache->rpt_type;
+        p_rpt->rpt_id = p_rpt_cache->rpt_id;
 
-//                 if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT ||
-//                     p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT ||
-//                     (p_rpt->uuid == GATT_UUID_HID_REPORT && p_rpt->rpt_type
-//                     == BTA_HH_RPTT_INPUT))
-//                 {
-//                     p_rpt->client_cfg_value =
-//                     GATT_CLT_CONFIG_NOTIFICATION;
-//                 }
-//             }
-//         }
-//     }
-// }
+        if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT ||
+            p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT ||
+            (p_rpt->uuid == GATT_UUID_HID_REPORT &&
+             p_rpt->rpt_type == BTA_HH_RPTT_INPUT)) {
+          p_rpt->client_cfg_value = GATT_CLT_CONFIG_NOTIFICATION;
+        }
+      }
+    }
+  }
+}
 
 #endif
diff --git a/bta/include/bta_api.h b/bta/include/bta_api.h
index d58f183..e6d7b5b 100644
--- a/bta/include/bta_api.h
+++ b/bta/include/bta_api.h
@@ -154,13 +154,6 @@
    BTM_SEC_OUT_AUTHENTICATE) /* Authentication required. */
 #define BTA_SEC_ENCRYPT \
   (BTM_SEC_IN_ENCRYPT | BTM_SEC_OUT_ENCRYPT) /* Encryption required. */
-#define BTA_SEC_MODE4_LEVEL4                                               \
-  (BTM_SEC_MODE4_LEVEL4) /* Mode 4 level 4 service, i.e. incoming/outgoing \
-                            MITM and P-256 encryption */
-#define BTA_SEC_MITM \
-  (BTM_SEC_IN_MITM | BTM_SEC_OUT_MITM) /* Man-In-The_Middle protection */
-#define BTA_SEC_IN_16_DIGITS \
-  (BTM_SEC_IN_MIN_16_DIGIT_PIN) /* Min 16 digit for pin code */
 
 typedef uint16_t tBTA_SEC;
 
@@ -1195,20 +1188,6 @@
  * Function         BTA_DmBond
  *
  * Description      This function initiates a bonding procedure with a peer
- *                  device.  The bonding procedure enables authentication
- *                  and optionally encryption on the Bluetooth link.
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTA_DmBond(const RawAddress& bd_addr);
-
-/*******************************************************************************
- *
- * Function         BTA_DmBondByTransport
- *
- * Description      This function initiates a bonding procedure with a peer
  *                  device by designated transport.  The bonding procedure
  *                  enables authentication and optionally encryption on the
  *                  Bluetooth link.
@@ -1217,8 +1196,8 @@
  * Returns          void
  *
  ******************************************************************************/
-extern void BTA_DmBondByTransport(const RawAddress& bd_addr,
-                                  tBTA_TRANSPORT transport);
+extern void BTA_DmBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                       tBTA_TRANSPORT transport, int device_type);
 
 /*******************************************************************************
  *
@@ -1477,66 +1456,6 @@
 
 /*******************************************************************************
  *
- * Function         BTA_DmSetBleConnScanParams
- *
- * Description      This function is called to set scan parameters used in
- *                  BLE connection request
- *
- * Parameters:      scan_interval    - scan interval
- *                  scan_window      - scan window
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTA_DmSetBleConnScanParams(uint32_t scan_interval,
-                                       uint32_t scan_window);
-
-/*******************************************************************************
- *
- * Function         BTA_DmSearchExt
- *
- * Description      This function searches for peer Bluetooth devices. It
- *                  performs an inquiry and gets the remote name for devices.
- *                  Service discovery is done if services is non zero
- *
- * Parameters       p_dm_inq: inquiry conditions
- *                  services: if service is not empty, service discovery will be
- *                            done.
- *                            for all GATT based service condition, put
- *                            num_uuid, and p_uuid is the pointer to the list of
- *                            UUID values.
- *                  p_cback: callback functino when search is completed.
- *
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTA_DmSearchExt(tBTA_DM_INQ* p_dm_inq,
-                            tBTA_SERVICE_MASK_EXT* p_services,
-                            tBTA_DM_SEARCH_CBACK* p_cback);
-
-/*******************************************************************************
- *
- * Function         BTA_DmDiscoverExt
- *
- * Description      This function does service discovery for services of a
- *                  peer device. When services.num_uuid is 0, it indicates all
- *                  GATT based services are to be searched; other wise a list of
- *                  UUID of interested services should be provided through
- *                  services.p_uuid.
- *
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTA_DmDiscoverExt(const RawAddress& bd_addr,
-                              tBTA_SERVICE_MASK_EXT* p_services,
-                              tBTA_DM_SEARCH_CBACK* p_cback, bool sdp_search);
-
-/*******************************************************************************
- *
  * Function         BTA_DmDiscoverByTransport
  *
  * Description      This function does service discovery on particular transport
diff --git a/bta/include/bta_av_api.h b/bta/include/bta_av_api.h
index 418f012..82be54d 100644
--- a/bta/include/bta_av_api.h
+++ b/bta/include/bta_av_api.h
@@ -72,6 +72,7 @@
 #define BTA_AV_FEAT_DELAY_RPT 0x0400 /* allow delay reporting */
 #define BTA_AV_FEAT_ACP_START \
   0x0800 /* start stream when 2nd SNK was accepted   */
+#define BTA_AV_FEAT_COVER_ARTWORK 0x1000 /* use cover art feature */
 #define BTA_AV_FEAT_APP_SETTING 0x2000 /* Player app setting support */
 
 /* Internal features */
@@ -144,8 +145,9 @@
 #define BTA_AV_OFFLOAD_START_RSP_EVT 22 /* a2dp offload start response */
 #define BTA_AV_RC_BROWSE_OPEN_EVT 23    /* remote control channel open */
 #define BTA_AV_RC_BROWSE_CLOSE_EVT 24   /* remote control channel closed */
+#define BTA_AV_RC_PSM_EVT 25            /* cover art psm update */
 /* Max BTA event */
-#define BTA_AV_MAX_EVT 25
+#define BTA_AV_MAX_EVT 26
 
 typedef uint8_t tBTA_AV_EVT;
 
@@ -234,6 +236,7 @@
 /* data associated with BTA_AV_RC_OPEN_EVT */
 typedef struct {
   uint8_t rc_handle;
+  uint16_t cover_art_psm;
   tBTA_AV_FEAT peer_features;
   RawAddress peer_addr;
   tBTA_AV_STATUS status;
@@ -265,6 +268,13 @@
   RawAddress peer_addr;
 } tBTA_AV_RC_FEAT;
 
+/* data associated with BTA_AV_RC_PSM_EVT */
+typedef struct {
+  uint8_t rc_handle;
+  uint16_t cover_art_psm;
+  RawAddress peer_addr;
+} tBTA_AV_RC_PSM;
+
 /* data associated with BTA_AV_REMOTE_CMD_EVT */
 typedef struct {
   uint8_t rc_handle;
@@ -342,6 +352,7 @@
   tBTA_AV_META_MSG meta_msg;
   tBTA_AV_REJECT reject;
   tBTA_AV_RC_FEAT rc_feat;
+  tBTA_AV_RC_PSM rc_cover_art_psm;
   tBTA_AV_STATUS status;
 } tBTA_AV;
 
@@ -360,7 +371,8 @@
 
 /* AV callback */
 typedef void(tBTA_AV_CBACK)(tBTA_AV_EVT event, tBTA_AV* p_data);
-typedef void(tBTA_AV_SINK_DATA_CBACK)(tBTA_AV_EVT event, tBTA_AV_MEDIA* p_data);
+typedef void(tBTA_AV_SINK_DATA_CBACK)(const RawAddress&, tBTA_AV_EVT event,
+                                      tBTA_AV_MEDIA* p_data);
 
 /* type for stream state machine action functions */
 struct tBTA_AV_SCB;
diff --git a/bta/include/bta_hf_client_api.h b/bta/include/bta_hf_client_api.h
old mode 100644
new mode 100755
index 897555d..7517461
--- a/bta/include/bta_hf_client_api.h
+++ b/bta/include/bta_hf_client_api.h
@@ -45,6 +45,7 @@
 #define BTA_HF_CLIENT_PEER_ECC 0x00000080 /* Enhanced Call Control */
 #define BTA_HF_CLIENT_PEER_EXTERR 0x00000100 /* Extended error codes */
 #define BTA_HF_CLIENT_PEER_CODEC 0x00000200  /* Codec Negotiation */
+#define BTA_HF_CLIENT_PEER_S4    0x00000800  /* ESCO S4 link setting */
 
 typedef uint16_t tBTA_HF_CLIENT_PEER_FEAT;
 
@@ -60,6 +61,7 @@
 #define BTA_HF_CLIENT_FEAT_ECS 0x00000020   /* Enhanced Call Status */
 #define BTA_HF_CLIENT_FEAT_ECC 0x00000040   /* Enhanced Call Control */
 #define BTA_HF_CLIENT_FEAT_CODEC 0x00000080 /* Codec Negotiation */
+#define BTA_HF_CLIENT_FEAT_S4  0x00000200   /* ESCO S4 link setting */
 
 /* HFP HF extended call handling - masks not related to any spec */
 #define BTA_HF_CLIENT_CHLD_REL \
diff --git a/bta/include/bta_pan_ci.h b/bta/include/bta_pan_ci.h
index 3711700..afc6377 100644
--- a/bta/include/bta_pan_ci.h
+++ b/bta/include/bta_pan_ci.h
@@ -67,8 +67,8 @@
  * Description      This function is called to enable or disable data flow on
  *                  the TX path.  The phone should call this function to
  *                  disable data flow when it is congested and cannot handle
- *                  any more data sent by bta_pan_co_tx_write() or
- *                  bta_pan_co_tx_writebuf().  This function is used when the
+ *                  any more data sent by bta_pan_co_tx_write().
+ *                  This function is used when the
  *                  TX data path is configured to use a push interface.
  *
  *
diff --git a/bta/include/bta_pan_co.h b/bta/include/bta_pan_co.h
index acd212e..6dc5435 100644
--- a/bta/include/bta_pan_co.h
+++ b/bta/include/bta_pan_co.h
@@ -38,7 +38,6 @@
 #define BTA_PAN_RX_PUSH_BUF 0x01 /* RX push with zero copy. */
 #define BTA_PAN_RX_PULL 0x02     /* RX pull. */
 #define BTA_PAN_TX_PUSH 0x00     /* TX push. */
-#define BTA_PAN_TX_PUSH_BUF 0x10 /* TX push with zero copy. */
 #define BTA_PAN_TX_PULL 0x20     /* TX pull. */
 
 /*****************************************************************************
@@ -140,24 +139,6 @@
 
 /*******************************************************************************
  *
- * Function         bta_pan_co_tx_writebuf
- *
- * Description      This function is called by PAN to send data to the phone
- *                  when the TX path is configured to use a push interface with
- *                  zero copy.  The phone must free the buffer using function
- *                  osi_free() when it is through processing the buffer.
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void bta_pan_co_tx_writebuf(uint16_t handle, uint8_t app_id,
-                                   const RawAddress& src, const RawAddress& dst,
-                                   uint16_t protocol, BT_HDR* p_buf, bool ext,
-                                   bool forward);
-
-/*******************************************************************************
- *
  * Function         bta_pan_co_rx_flow
  *
  * Description      This function is called by PAN to enable or disable
diff --git a/bta/jv/bta_jv_act.cc b/bta/jv/bta_jv_act.cc
index 5cd8041..f582b78 100644
--- a/bta/jv/bta_jv_act.cc
+++ b/bta/jv/bta_jv_act.cc
@@ -362,6 +362,8 @@
   p_cb->cong = false;
   bta_jv_free_sec_id(&p_cb->sec_id);
   p_cb->p_cback = NULL;
+  p_cb->handle = 0;
+  p_cb->l2cap_socket_id = 0;
   return status;
 }
 
@@ -1121,7 +1123,7 @@
 /* stops an L2CAP server */
 void bta_jv_l2cap_stop_server(uint16_t local_psm, uint32_t l2cap_socket_id) {
   for (int i = 0; i < BTA_JV_MAX_L2C_CONN; i++) {
-    if (bta_jv_cb.l2c_cb[i].psm == local_psm) {
+    if (bta_jv_cb.l2c_cb[i].l2cap_socket_id == l2cap_socket_id) {
       tBTA_JV_L2C_CB* p_cb = &bta_jv_cb.l2c_cb[i];
       tBTA_JV_L2CAP_CBACK* p_cback = p_cb->p_cback;
       tBTA_JV_L2CAP_CLOSE evt_data;
@@ -1620,6 +1622,9 @@
         p_pcb->handle = BTA_JV_RFC_H_S_TO_HDL(p_cb->handle, si);
         VLOG(2) << __func__ << ": p_pcb->handle=" << loghex(p_pcb->handle)
                 << ", curr_sess=" << p_cb->curr_sess;
+      } else {
+        LOG(ERROR) << __func__ << ": RFCOMM_CreateConnection failed";
+        return NULL;
       }
     } else {
       LOG(ERROR) << __func__ << ": cannot create new rfc listen port";
@@ -1885,15 +1890,6 @@
   static tL2CAP_FIXED_CHNL_REG fcr = {
       .pL2CA_FixedConn_Cb = fcchan_conn_chng_cbk,
       .pL2CA_FixedData_Cb = fcchan_data_cbk,
-      .fixed_chnl_opts =
-          {
-              .mode = L2CAP_FCR_BASIC_MODE,
-              .tx_win_sz = 1,
-              .max_transmit = 0xFF,
-              .rtrans_tout = 2000,
-              .mon_tout = 12000,
-              .mps = 670,
-          },
       .default_idle_tout = 0xffff,
   };
 
diff --git a/bta/pan/bta_pan_act.cc b/bta/pan/bta_pan_act.cc
index 548d6fd..f887cba 100644
--- a/bta/pan/bta_pan_act.cc
+++ b/bta/pan/bta_pan_act.cc
@@ -598,48 +598,19 @@
  *
  ******************************************************************************/
 void bta_pan_tx_path(tBTA_PAN_SCB* p_scb, UNUSED_ATTR tBTA_PAN_DATA* p_data) {
-  /* if data path configured for tx pull */
-  if ((bta_pan_cb.flow_mask & BTA_PAN_TX_MASK) == BTA_PAN_TX_PULL) {
-    bta_pan_pm_conn_busy(p_scb);
-    /* call application callout function for tx path */
-    bta_pan_co_tx_path(p_scb->handle, p_scb->app_id);
+  bta_pan_pm_conn_busy(p_scb);
+  /* call application callout function for tx path */
+  bta_pan_co_tx_path(p_scb->handle, p_scb->app_id);
 
-    /* free data that exceeds queue level */
-    while (fixed_queue_length(p_scb->data_queue) > bta_pan_cb.q_level)
-      osi_free(fixed_queue_try_dequeue(p_scb->data_queue));
-    bta_pan_pm_conn_idle(p_scb);
-  }
-  /* if configured for zero copy push */
-  else if ((bta_pan_cb.flow_mask & BTA_PAN_TX_MASK) == BTA_PAN_TX_PUSH_BUF) {
-    /* if app can accept data */
-    if (p_scb->app_flow_enable) {
-      BT_HDR* p_buf;
-
-      /* read data from the queue */
-      p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_scb->data_queue);
-      if (p_buf != NULL) {
-        /* send data to application */
-        bta_pan_co_tx_writebuf(p_scb->handle, p_scb->app_id,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->src,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->dst,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->protocol, p_buf,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->ext,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->forward);
-      }
-      /* free data that exceeds queue level  */
-      while (fixed_queue_length(p_scb->data_queue) > bta_pan_cb.q_level)
-        osi_free(fixed_queue_try_dequeue(p_scb->data_queue));
-
-      /* if there is more data to be passed to
-      upper layer */
-      if (!fixed_queue_is_empty(p_scb->data_queue)) {
-        p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
-        p_buf->layer_specific = p_scb->handle;
-        p_buf->event = BTA_PAN_RX_FROM_BNEP_READY_EVT;
-        bta_sys_sendmsg(p_buf);
-      }
+  /* free data that exceeds queue level */
+  while (fixed_queue_length(p_scb->data_queue) > bta_pan_cb.q_level) {
+    BT_HDR* p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_scb->data_queue);
+    if (p_buf != nullptr) {
+      osi_free(p_buf);
     }
   }
+
+  bta_pan_pm_conn_idle(p_scb);
 }
 
 /*******************************************************************************
diff --git a/bta/pan/bta_pan_ci.cc b/bta/pan/bta_pan_ci.cc
index d2fda39..7ebb0ad 100644
--- a/bta/pan/bta_pan_ci.cc
+++ b/bta/pan/bta_pan_ci.cc
@@ -89,8 +89,8 @@
  * Description      This function is called to enable or disable data flow on
  *                  the TX path.  The phone should call this function to
  *                  disable data flow when it is congested and cannot handle
- *                  any more data sent by bta_pan_co_tx_write() or
- *                  bta_pan_co_tx_writebuf().  This function is used when the
+ *                  any more data sent by bta_pan_co_tx_write().
+ *                  This function is used when the
  *                  TX data path is configured to use a push interface.
  *
  *
diff --git a/bta/sys/bta_sys.h b/bta/sys/bta_sys.h
index 29e9a3f..3ceefc1 100644
--- a/bta/sys/bta_sys.h
+++ b/bta/sys/bta_sys.h
@@ -222,6 +222,7 @@
 extern bool bta_sys_is_register(uint8_t id);
 extern uint16_t bta_sys_get_sys_features(void);
 extern void bta_sys_sendmsg(void* p_msg);
+extern void bta_sys_sendmsg_delayed(void* p_msg, const base::TimeDelta& delay);
 extern void bta_sys_start_timer(alarm_t* alarm, uint64_t interval_ms,
                                 uint16_t event, uint16_t layer_specific);
 extern void bta_sys_disable(tBTA_SYS_HW_MODULE module);
diff --git a/bta/sys/bta_sys_main.cc b/bta/sys/bta_sys_main.cc
index 1036e74..e81320d 100644
--- a/bta/sys/bta_sys_main.cc
+++ b/bta/sys/bta_sys_main.cc
@@ -532,6 +532,14 @@
   }
 }
 
+void bta_sys_sendmsg_delayed(void* p_msg, const base::TimeDelta& delay) {
+  if (do_in_main_thread_delayed(
+          FROM_HERE, base::Bind(&bta_sys_event, static_cast<BT_HDR*>(p_msg)),
+          delay) != BT_STATUS_SUCCESS) {
+    LOG(ERROR) << __func__ << ": do_in_main_thread_delayed failed";
+  }
+}
+
 /*******************************************************************************
  *
  * Function         bta_sys_start_timer
diff --git a/bta/test/bta_hf_client_add_record_test.cc b/bta/test/bta_hf_client_add_record_test.cc
new file mode 100644
index 0000000..fa6e4d6
--- /dev/null
+++ b/bta/test/bta_hf_client_add_record_test.cc
@@ -0,0 +1,55 @@
+#include <base/logging.h>
+#include <gtest/gtest.h>
+
+#include "bta/hf_client/bta_hf_client_sdp.cc"
+#include "btif/src/btif_hf_client.cc"
+
+static uint16_t gVersion;
+
+void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
+bool SDP_AddProtocolList(uint32_t handle, uint16_t num_elem,
+                         tSDP_PROTOCOL_ELEM* p_elem_list) {
+  return false;
+}
+bool SDP_AddServiceClassIdList(uint32_t handle, uint16_t num_services,
+                               uint16_t* p_service_uuids) {
+  return false;
+}
+bool SDP_AddProfileDescriptorList(uint32_t handle, uint16_t profile_uuid,
+                                  uint16_t version) {
+  gVersion = version;
+  return false;
+}
+bool SDP_AddAttribute(uint32_t handle, uint16_t attr_id, uint8_t attr_type,
+                      uint32_t attr_len, uint8_t* p_val) {
+  return false;
+}
+bool SDP_AddUuidSequence(uint32_t handle, uint16_t attr_id, uint16_t num_uuids,
+                         uint16_t* p_uuids) {
+  return false;
+}
+
+class BtaHfClientAddRecordTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    gVersion = 0;
+  }
+
+  void TearDown() override {}
+};
+
+TEST_F(BtaHfClientAddRecordTest, test_hf_client_add_record) {
+  tBTA_HF_CLIENT_FEAT features = BTIF_HF_CLIENT_FEATURES;
+  uint32_t sdp_handle = 0;
+  uint8_t scn = 0;
+
+  osi_property_set("persist.bluetooth.hfpclient.sco_s4_supported", "true");
+  bta_hf_client_add_record("Handsfree", scn, features, sdp_handle);
+  EXPECT_EQ(gVersion, 0x0107);
+  sdp_handle++;
+  scn++;
+  osi_property_set("persist.bluetooth.hfpclient.sco_s4_supported", "false");
+  bta_hf_client_add_record("Handsfree", scn, features, sdp_handle);
+  EXPECT_EQ(gVersion, 0x0106);
+}
+
diff --git a/btcore/src/module.cc b/btcore/src/module.cc
index d226006..856676b 100644
--- a/btcore/src/module.cc
+++ b/btcore/src/module.cc
@@ -65,8 +65,7 @@
   CHECK(get_module_state(module) == MODULE_STATE_NONE);
 
   if (!call_lifecycle_function(module->init)) {
-    LOG_ERROR(LOG_TAG, "%s Failed to initialize module \"%s\"", __func__,
-              module->name);
+    LOG_ERROR("%s Failed to initialize module \"%s\"", __func__, module->name);
     return false;
   }
 
@@ -84,13 +83,12 @@
   CHECK(get_module_state(module) == MODULE_STATE_INITIALIZED ||
         module->init == NULL);
 
-  LOG_INFO(LOG_TAG, "%s Starting module \"%s\"", __func__, module->name);
+  LOG_INFO("%s Starting module \"%s\"", __func__, module->name);
   if (!call_lifecycle_function(module->start_up)) {
-    LOG_ERROR(LOG_TAG, "%s Failed to start up module \"%s\"", __func__,
-              module->name);
+    LOG_ERROR("%s Failed to start up module \"%s\"", __func__, module->name);
     return false;
   }
-  LOG_INFO(LOG_TAG, "%s Started module \"%s\"", __func__, module->name);
+  LOG_INFO("%s Started module \"%s\"", __func__, module->name);
 
   set_module_state(module, MODULE_STATE_STARTED);
   return true;
@@ -104,14 +102,12 @@
   // Only something to do if the module was actually started
   if (state < MODULE_STATE_STARTED) return;
 
-  LOG_INFO(LOG_TAG, "%s Shutting down module \"%s\"", __func__, module->name);
+  LOG_INFO("%s Shutting down module \"%s\"", __func__, module->name);
   if (!call_lifecycle_function(module->shut_down)) {
-    LOG_ERROR(LOG_TAG,
-              "%s Failed to shutdown module \"%s\". Continuing anyway.",
+    LOG_ERROR("%s Failed to shutdown module \"%s\". Continuing anyway.",
               __func__, module->name);
   }
-  LOG_INFO(LOG_TAG, "%s Shutdown of module \"%s\" completed", __func__,
-           module->name);
+  LOG_INFO("%s Shutdown of module \"%s\" completed", __func__, module->name);
 
   set_module_state(module, MODULE_STATE_INITIALIZED);
 }
@@ -124,13 +120,12 @@
   // Only something to do if the module was actually initialized
   if (state < MODULE_STATE_INITIALIZED) return;
 
-  LOG_INFO(LOG_TAG, "%s Cleaning up module \"%s\"", __func__, module->name);
+  LOG_INFO("%s Cleaning up module \"%s\"", __func__, module->name);
   if (!call_lifecycle_function(module->clean_up)) {
-    LOG_ERROR(LOG_TAG, "%s Failed to cleanup module \"%s\". Continuing anyway.",
+    LOG_ERROR("%s Failed to cleanup module \"%s\". Continuing anyway.",
               __func__, module->name);
   }
-  LOG_INFO(LOG_TAG, "%s Cleanup of module \"%s\" completed", __func__,
-           module->name);
+  LOG_INFO("%s Cleanup of module \"%s\" completed", __func__, module->name);
 
   set_module_state(module, MODULE_STATE_NONE);
 }
diff --git a/btif/Android.bp b/btif/Android.bp
old mode 100644
new mode 100755
index 7f5bdc2..dd24121
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -24,8 +24,6 @@
     "system/bt/utils/include",
     "system/bt/include",
     "system/libhwbinder/include",
-    //"system/security/keystore/include",
-    //"hardware/interfaces/keymaster/4.0/support/include",
 ]
 
 // libbtif static library for target
@@ -57,6 +55,7 @@
         "src/btif_ble_scanner.cc",
         "src/btif_bqr.cc",
         "src/btif_config.cc",
+        "src/btif_config_cache.cc",
         "src/btif_config_transcode.cc",
         "src/btif_core.cc",
         "src/btif_debug.cc",
@@ -73,7 +72,6 @@
         "src/btif_hf_client.cc",
         "src/btif_hh.cc",
         "src/btif_hd.cc",
-        //"src/btif_keystore.cc",
         "src/btif_mce.cc",
         "src/btif_pan.cc",
         "src/btif_profile_queue.cc",
@@ -90,6 +88,7 @@
         "src/btif_storage.cc",
         "src/btif_uid.cc",
         "src/btif_util.cc",
+        "src/btif_keystore.cc",
         "src/stack_manager.cc",
     ],
     header_libs: [
@@ -107,12 +106,6 @@
         "libhidlbase",
         "libutils",
         "libcrypto",
-        //"android.hardware.keymaster@4.0",
-        //"android.hardware.keymaster@3.0",
-        //"libkeymaster4support",
-        //"libkeystore_aidl",
-        //"libkeystore_binder",
-        //"libkeystore_parcelables",
     ],
     whole_static_libs: [
         "avrcp-target-service",
@@ -135,11 +128,11 @@
     include_dirs: btifCommonIncludes,
     srcs: [
         "test/btif_storage_test.cc",
-        //"test/btif_keystore_test.cc"
     ],
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
         "libaaudio",
+        "android.hardware.bluetooth@1.0",
         "android.hardware.bluetooth.a2dp@1.0",
         "android.hardware.bluetooth.audio@2.0",
         "libfmq",
@@ -150,13 +143,6 @@
         "libprocessgroup",
         "libutils",
         "libcrypto",
-        //"android.hardware.keymaster@4.0",
-        //"android.hardware.keymaster@3.0",
-        //"libkeymaster4support",
-        //"libkeystore_aidl",
-        //"libkeystore_binder",
-        //"libkeystore_parcelables",
-        //"libbinder",
     ],
     static_libs: [
         "libbt-bta",
@@ -174,7 +160,7 @@
         "libbluetooth-types",
         "libosi",
         "libbt-protos-lite",
-    ],
+   ],
     whole_static_libs: [
         "libbtif",
         "libbluetooth-for-tests",
@@ -207,3 +193,84 @@
     ],
     cflags: ["-DBUILDCFG"],
 }
+
+// btif rc unit tests for target
+// ========================================================
+cc_test {
+    name: "net_test_btif_rc",
+    defaults: ["fluoride_defaults"],
+    test_suites: ["device-tests"],
+    host_supported: true,
+    include_dirs: btifCommonIncludes,
+    srcs: [
+        "test/btif_rc_test.cc",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    shared_libs: [
+        "libcrypto",
+        "libcutils",
+        "liblog",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libbt-common",
+        "libbt-protos-lite",
+        "libosi",
+        "libosi-AllocationTestHarness",
+    ],
+    cflags: ["-DBUILDCFG"],
+    sanitize: {
+        address: true,
+        cfi: true,
+        misc_undefined: ["bounds"],
+    },
+}
+
+// btif config cache unit tests for target
+// ========================================================
+cc_test {
+    name: "net_test_btif_config_cache",
+    defaults: ["fluoride_defaults"],
+    test_suites: ["device-tests"],
+    host_supported: true,
+    include_dirs: btifCommonIncludes,
+    srcs: [
+        "src/btif_config_cache.cc",
+        "test/btif_config_cache_test.cc",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    shared_libs: [
+        "liblog",
+        "libcutils",
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libosi",
+        "libgmock",
+        "libc++fs",
+    ],
+    cflags: ["-DBUILDCFG"],
+}
+
+// btif hf client service tests for target
+// ========================================================
+cc_test {
+    name: "net_test_btif_hf_client_service",
+    defaults: ["fluoride_defaults"],
+    test_suites: ["device-tests"],
+    include_dirs: btifCommonIncludes,
+    srcs: [
+        "test/btif_hf_client_service_test.cc",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libosi",
+    ],
+    cflags: ["-DBUILDCFG"],
+}
diff --git a/btif/co/bta_av_co.cc b/btif/co/bta_av_co.cc
index f3d39e2..708bb40 100644
--- a/btif/co/bta_av_co.cc
+++ b/btif/co/bta_av_co.cc
@@ -417,10 +417,14 @@
    *
    * @param peer_address the peer address
    * @param codec_user_config the codec user configuration to set
+   * @param p_restart_output if there is a change in the encoder configuration
+   * that requires restarting of the A2DP connection, flag |p_restart_output|
+   * will be set to true.
    * @return true on success, otherwise false
    */
   bool SetCodecUserConfig(const RawAddress& peer_address,
-                          const btav_a2dp_codec_config_t& codec_user_config);
+                          const btav_a2dp_codec_config_t& codec_user_config,
+                          bool* p_restart_output);
 
   /**
    * Set the codec audio configuration.
@@ -954,8 +958,9 @@
       (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
     return A2DP_FAIL;
   }
-  APPL_TRACE_DEBUG("%s: last Sink codec reached for peer %s", __func__,
-                   p_peer->addr.ToString().c_str());
+  APPL_TRACE_DEBUG("%s: last Sink codec reached for peer %s (local %s)",
+                   __func__, p_peer->addr.ToString().c_str(),
+                   p_peer->acceptor ? "acceptor" : "initiator");
 
   // Select the Source codec
   const BtaAvCoSep* p_sink = nullptr;
@@ -973,6 +978,32 @@
       return A2DP_FAIL;
     }
   } else {
+    if (btif_av_peer_prefers_mandatory_codec(p_peer->addr)) {
+      // Apply user preferred codec directly before first codec selected.
+      p_sink = FindPeerSink(p_peer, BTAV_A2DP_CODEC_INDEX_SOURCE_SBC);
+      if (p_sink != nullptr) {
+        APPL_TRACE_API("%s: mandatory codec preferred for peer %s", __func__,
+                       p_peer->addr.ToString().c_str());
+        btav_a2dp_codec_config_t high_priority_mandatory{
+            .codec_type = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC,
+            .codec_priority = BTAV_A2DP_CODEC_PRIORITY_HIGHEST,
+            // Using default settings for those untouched fields
+        };
+        uint8_t result_codec_config[AVDT_CODEC_SIZE];
+        bool restart_input = false;
+        bool restart_output = false;
+        bool config_updated = false;
+        tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
+        GetPeerEncoderParameters(p_peer->addr, &peer_params);
+        p_peer->GetCodecs()->setCodecUserConfig(
+            high_priority_mandatory, &peer_params, p_sink->codec_caps,
+            result_codec_config, &restart_input, &restart_output,
+            &config_updated);
+      } else {
+        APPL_TRACE_WARNING("%s: mandatory codec not found for peer %s",
+                           __func__, p_peer->addr.ToString().c_str());
+      }
+    }
     p_sink = SelectSourceCodec(p_peer);
     if (p_sink == nullptr) {
       APPL_TRACE_ERROR("%s: cannot set up codec for peer %s", __func__,
@@ -1374,36 +1405,22 @@
   APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x delay:0x%x", __func__,
                    peer_address.ToString().c_str(), bta_av_handle, delay);
 
-  btif_av_set_audio_delay(delay);
+  btif_av_set_audio_delay(peer_address, delay);
 }
 
 void BtaAvCo::UpdateMtu(tBTA_AV_HNDL bta_av_handle,
                         const RawAddress& peer_address, uint16_t mtu) {
-  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x mtu: %d", __func__,
-                   peer_address.ToString().c_str(), bta_av_handle, mtu);
+  LOG(INFO) << __func__ << ": peer " << peer_address
+            << " bta_av_handle: " << loghex(bta_av_handle) << " mtu: " << mtu;
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    APPL_TRACE_ERROR(
-        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
-        __func__, bta_av_handle, peer_address.ToString().c_str());
+    LOG(ERROR) << __func__ << ": could not find peer entry for bta_av_handle "
+               << loghex(bta_av_handle) << " peer " << peer_address;
     return;
   }
-
-  if (p_peer->mtu == mtu) return;
-
   p_peer->mtu = mtu;
-  if (active_peer_ == p_peer) {
-    LOG(INFO) << __func__ << ": update the codec encoder with peer "
-              << peer_address << " bta_av_handle: " << loghex(bta_av_handle)
-              << ", new MTU: " << mtu;
-    // Send a request with NONE config values to update only the MTU.
-    SetCodecAudioConfig(
-        {.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE,
-         .bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE,
-         .channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE});
-  }
 }
 
 bool BtaAvCo::SetActivePeer(const RawAddress& peer_address) {
@@ -1472,7 +1489,7 @@
 
 bool BtaAvCo::SetCodecUserConfig(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config) {
+    const btav_a2dp_codec_config_t& codec_user_config, bool* p_restart_output) {
   uint8_t result_codec_config[AVDT_CODEC_SIZE];
   const BtaAvCoSep* p_sink = nullptr;
   bool restart_input = false;
@@ -1483,6 +1500,8 @@
   VLOG(1) << __func__ << ": peer_address=" << peer_address
           << " codec_user_config={" << codec_user_config.ToString() << "}";
 
+  *p_restart_output = false;
+
   BtaAvCoPeer* p_peer = FindPeer(peer_address);
   if (p_peer == nullptr) {
     LOG(ERROR) << __func__ << ": cannot find peer " << peer_address
@@ -1544,6 +1563,7 @@
             << loghex(p_peer->BtaAvHandle()) << ")";
     BTA_AvReconfig(p_peer->BtaAvHandle(), true, p_sink->sep_info_idx,
                    p_peer->codec_config, num_protect, bta_av_co_cp_scmst);
+    *p_restart_output = true;
   }
 
 done:
@@ -2177,8 +2197,9 @@
 
 bool bta_av_co_set_codec_user_config(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config) {
-  return bta_av_co_cb.SetCodecUserConfig(peer_address, codec_user_config);
+    const btav_a2dp_codec_config_t& codec_user_config, bool* p_restart_output) {
+  return bta_av_co_cb.SetCodecUserConfig(peer_address, codec_user_config,
+                                         p_restart_output);
 }
 
 bool bta_av_co_set_codec_audio_config(
diff --git a/btif/co/bta_pan_co.cc b/btif/co/bta_pan_co.cc
index a799d93..86499f3 100644
--- a/btif/co/bta_pan_co.cc
+++ b/btif/co/bta_pan_co.cc
@@ -235,29 +235,6 @@
 
 /*******************************************************************************
  *
- * Function         bta_pan_co_tx_writebuf
- *
- * Description      This function is called by PAN to send data to the phone
- *                  when the TX path is configured to use a push interface with
- *                  zero copy.  The phone must free the buffer using function
- *                  osi_free() when it is through processing the buffer.
- *
- *
- * Returns          true if flow enabled
- *
- ******************************************************************************/
-void bta_pan_co_tx_writebuf(UNUSED_ATTR uint16_t handle,
-                            UNUSED_ATTR uint8_t app_id,
-                            UNUSED_ATTR const RawAddress& src,
-                            UNUSED_ATTR const RawAddress& dst,
-                            UNUSED_ATTR uint16_t protocol,
-                            UNUSED_ATTR BT_HDR* p_buf, UNUSED_ATTR bool ext,
-                            UNUSED_ATTR bool forward) {
-  BTIF_TRACE_API("bta_pan_co_tx_writebuf not used");
-}
-
-/*******************************************************************************
- *
  * Function         bta_pan_co_rx_flow
  *
  * Description      This function is called by PAN to enable or disable
diff --git a/btif/include/btif_a2dp_sink.h b/btif/include/btif_a2dp_sink.h
index 0a9038c..b972c79 100644
--- a/btif/include/btif_a2dp_sink.h
+++ b/btif/include/btif_a2dp_sink.h
@@ -109,6 +109,9 @@
 // |tBTA_AV_SUSPEND|.
 void btif_a2dp_sink_on_suspended(tBTA_AV_SUSPEND* p_av_suspend);
 
+// Start the decoder for the A2DP Sink module.
+bool btif_a2dp_sink_on_start(void);
+
 // Enable/disable discarding of received A2DP frames.
 // If |enable| is true, the discarding is enabled, otherwise is disabled.
 void btif_a2dp_sink_set_rx_flush(bool enable);
diff --git a/btif/include/btif_a2dp_source.h b/btif/include/btif_a2dp_source.h
index 0649012..6d2cbb1 100644
--- a/btif/include/btif_a2dp_source.h
+++ b/btif/include/btif_a2dp_source.h
@@ -89,7 +89,8 @@
 // |codec_user_config| contains the preferred codec user configuration.
 void btif_a2dp_source_encoder_user_config_update_req(
     const RawAddress& peer_addr,
-    const btav_a2dp_codec_config_t& codec_user_config);
+    const std::vector<btav_a2dp_codec_config_t>& codec_user_preferences,
+    std::promise<void> peer_ready_promise);
 
 // Process a request to update the A2DP audio encoding with new audio
 // configuration feeding parameters stored in |codec_audio_config|.
diff --git a/btif/include/btif_api.h b/btif/include/btif_api.h
index cef1aad..31c5e77 100644
--- a/btif/include/btif_api.h
+++ b/btif/include/btif_api.h
@@ -105,7 +105,7 @@
 
 /*******************************************************************************
  *
- * Function         is_single_user_mode_
+ * Function         is_niap_mode_
  *
  * Description      Checks if BT was enabled in single user mode. In this
  *                  mode, use of keystore for key attestation of LTK is limitee
@@ -114,7 +114,23 @@
  * Returns          bool
  *
  ******************************************************************************/
-bool is_single_user_mode(void);
+bool is_niap_mode(void);
+
+/*******************************************************************************
+ *
+ * Function         get_niap_config_compare_result
+ *
+ * Description      Get the niap config compare result for confirming the config
+ *                  checksum compare result. When the niap mode doesn't enable,
+ *                  it should be all pass (0b11).
+ *                  Bit define:
+ *                    CONFIG_FILE_COMPARE_PASS = 0b01
+ *                    CONFIG_BACKUP_COMPARE_PASS = 0b10
+ *
+ * Returns          int
+ *
+ ******************************************************************************/
+int get_niap_config_compare_result(void);
 
 /*******************************************************************************
  *
diff --git a/btif/include/btif_av.h b/btif/include/btif_av.h
index e42f056..8ddfc6a 100644
--- a/btif/include/btif_av.h
+++ b/btif/include/btif_av.h
@@ -114,6 +114,14 @@
 bool btif_av_peer_supports_3mbps(const RawAddress& peer_address);
 
 /**
+ * Check whether the mandatory codec is more preferred for this peer.
+ *
+ * @param peer_address the target peer address
+ * @return true if optional codecs are not preferred to be used
+ */
+bool btif_av_peer_prefers_mandatory_codec(const RawAddress& peer_address);
+
+/**
  * Report A2DP Source Codec State for a peer.
  *
  * @param peer_address the address of the peer to report
@@ -163,9 +171,16 @@
 /**
  * Set the audio delay for the stream.
  *
+ * @param peer_address the address of the peer to report
  * @param delay the delay to set in units of 1/10ms
  */
-void btif_av_set_audio_delay(uint16_t delay);
+void btif_av_set_audio_delay(const RawAddress& peer_address, uint16_t delay);
+
+/**
+ * Get the audio delay for the stream.
+ *  @param  none
+ */
+uint16_t btif_av_get_audio_delay(void);
 
 /**
  * Reset the audio delay and count of audio bytes sent to zero.
@@ -188,6 +203,12 @@
 bool btif_av_is_a2dp_offload_enabled(void);
 
 /**
+ *  check A2DP offload enabled and running
+ *  @param  none
+ */
+bool btif_av_is_a2dp_offload_running(void);
+
+/**
  * Check whether peer device is silenced
  *
  * @param peer_address to check
diff --git a/btif/include/btif_av_co.h b/btif/include/btif_av_co.h
index 93d6d7e..01b3b2f 100644
--- a/btif/include/btif_av_co.h
+++ b/btif/include/btif_av_co.h
@@ -48,10 +48,11 @@
 // Sets the user preferred codec configuration.
 // The peer address is |peer_addr|.
 // |codec_user_config| contains the preferred codec configuration.
+// |restart_output| is used to know whether AV is reconfiguring with remote.
 // Returns true on success, otherwise false.
 bool bta_av_co_set_codec_user_config(
     const RawAddress& peer_addr,
-    const btav_a2dp_codec_config_t& codec_user_config);
+    const btav_a2dp_codec_config_t& codec_user_config, bool* p_restart_output);
 
 // Sets the Audio HAL selected audio feeding parameters.
 // Those parameters are applied only to the currently selected codec.
diff --git a/btif/include/btif_bqr.h b/btif/include/btif_bqr.h
index 4407b0d..7dcff60 100644
--- a/btif/include/btif_bqr.h
+++ b/btif/include/btif_bqr.h
@@ -19,6 +19,7 @@
 
 #include "btm_api_types.h"
 #include "common/leaky_bonded_queue.h"
+#include "osi/include/osi.h"
 
 namespace bluetooth {
 namespace bqr {
@@ -26,8 +27,7 @@
 // Bluetooth Quality Report (BQR)
 //
 // It is a feature to start the mechanism in the Bluetooth controller to report
-// Bluetooth Quality event to the host and there are four options could be
-// enabled:
+// Bluetooth Quality event to the host and the following options can be enabled:
 //   [Quality Monitoring Mode]
 //     The controller shall periodically send Bluetooth Quality Report sub-event
 //     to the host.
@@ -44,6 +44,25 @@
 //   [(e)SCO Voice Choppy]
 //     When the controller detects the factors which will cause voice choppy,
 //     the controller shall report (e)SCO Voice Choppy event to the host.
+//
+//   [Root Inflammation]
+//     When the controller encounters an error it shall report Root Inflammation
+//     event indicating the error code to the host.
+//
+//   [LMP/LL message trace]
+//     The controller sends the LMP/LL message handshaking with the remote
+//     device to the host.
+//
+//   [Bluetooth Multi-profile/Coex scheduling trace]
+//     The controller sends its scheduling information on handling the Bluetooth
+//     multiple profiles and wireless coexistence in the 2.4 Ghz band to the
+//     host.
+//
+//   [Enable the Controller Debug Information mechanism]
+//     After enabling the Controller Debug Information mechanism, the controller
+//     just can autonomously report debug logging information via the Controller
+//     Debug Info sub-event to the host.
+//
 
 // Bit masks for the selected quality event reporting.
 static constexpr uint32_t kQualityEventMaskAllOff = 0;
@@ -51,16 +70,31 @@
 static constexpr uint32_t kQualityEventMaskApproachLsto = 0x00000002;
 static constexpr uint32_t kQualityEventMaskA2dpAudioChoppy = 0x00000004;
 static constexpr uint32_t kQualityEventMaskScoVoiceChoppy = 0x00000008;
+static constexpr uint32_t kQualityEventMaskRootInflammation = 0x00000010;
+static constexpr uint32_t kQualityEventMaskLmpMessageTrace = 0x00010000;
+static constexpr uint32_t kQualityEventMaskBtSchedulingTrace = 0x00020000;
+static constexpr uint32_t kQualityEventMaskControllerDbgInfo = 0x00040000;
 static constexpr uint32_t kQualityEventMaskAll =
     kQualityEventMaskMonitorMode | kQualityEventMaskApproachLsto |
-    kQualityEventMaskA2dpAudioChoppy | kQualityEventMaskScoVoiceChoppy;
+    kQualityEventMaskA2dpAudioChoppy | kQualityEventMaskScoVoiceChoppy |
+    kQualityEventMaskRootInflammation | kQualityEventMaskLmpMessageTrace |
+    kQualityEventMaskBtSchedulingTrace | kQualityEventMaskControllerDbgInfo;
 // Define the minimum time interval (in ms) of quality event reporting for the
 // selected quality event(s). Controller Firmware should not report the next
 // event within the defined time interval.
 static constexpr uint16_t kMinReportIntervalNoLimit = 0;
 static constexpr uint16_t kMinReportIntervalMaxMs = 0xFFFF;
-// Total length of all BQR parameters except Vendor Specific Parameters.
-static constexpr uint8_t kBqrParamTotalLen = 48;
+// The maximum count of Log Dump related event can be written in the log file.
+static constexpr uint16_t kLogDumpEventPerFile = 0x00FF;
+// Total length of all parameters of the link Quality related event except
+// Vendor Specific Parameters.
+static constexpr uint8_t kLinkQualityParamTotalLen = 48;
+// Total length of all parameters of the ROOT_INFLAMMATION event except Vendor
+// Specific Parameters.
+static constexpr uint8_t kRootInflammationParamTotalLen = 3;
+// Total length of all parameters of the Log Dump related event except Vendor
+// Specific Parameters.
+static constexpr uint8_t kLogDumpParamTotalLen = 3;
 // Warning criteria of the RSSI value.
 static constexpr int8_t kCriWarnRssi = -80;
 // Warning criteria of the unused AFH channel count.
@@ -73,6 +107,27 @@
 // The Property of BQR minimum report interval configuration.
 static constexpr const char* kpPropertyMinReportIntervalMs =
     "persist.bluetooth.bqr.min_interval_ms";
+// Path of the LMP/LL message trace log file.
+static constexpr const char* kpLmpLlMessageTraceLogPath =
+    "/data/misc/bluetooth/logs/lmp_ll_message_trace.log";
+// Path of the last LMP/LL message trace log file.
+static constexpr const char* kpLmpLlMessageTraceLastLogPath =
+    "/data/misc/bluetooth/logs/lmp_ll_message_trace.log.last";
+// Path of the Bluetooth Multi-profile/Coex scheduling trace log file.
+static constexpr const char* kpBtSchedulingTraceLogPath =
+    "/data/misc/bluetooth/logs/bt_scheduling_trace.log";
+// Path of the last Bluetooth Multi-profile/Coex scheduling trace log file.
+static constexpr const char* kpBtSchedulingTraceLastLogPath =
+    "/data/misc/bluetooth/logs/bt_scheduling_trace.log.last";
+
+// File Descriptor of LMP/LL message trace log
+static int LmpLlMessageTraceLogFd = INVALID_FD;
+// File Descriptor of Bluetooth Multi-profile/Coex scheduling trace log
+static int BtSchedulingTraceLogFd = INVALID_FD;
+// Counter of LMP/LL message trace
+static uint16_t LmpLlMessageTraceCounter = 0;
+// Counter of Bluetooth Multi-profile/Coex scheduling trace
+static uint16_t BtSchedulingTraceCounter = 0;
 
 // Action definition
 //
@@ -90,7 +145,11 @@
   QUALITY_REPORT_ID_MONITOR_MODE = 0x01,
   QUALITY_REPORT_ID_APPROACH_LSTO = 0x02,
   QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY = 0x03,
-  QUALITY_REPORT_ID_SCO_VOICE_CHOPPY = 0x04
+  QUALITY_REPORT_ID_SCO_VOICE_CHOPPY = 0x04,
+  QUALITY_REPORT_ID_ROOT_INFLAMMATION = 0x05,
+  QUALITY_REPORT_ID_LMP_LL_MESSAGE_TRACE = 0x11,
+  QUALITY_REPORT_ID_BT_SCHEDULING_TRACE = 0x12,
+  QUALITY_REPORT_ID_CONTROLLER_DBG_INFO = 0x13
 };
 
 // Packet Type definition
@@ -132,17 +191,92 @@
   uint16_t minimum_report_interval_ms;
 } BqrConfiguration;
 
+// Link quality related BQR event
+typedef struct {
+  // Quality report ID.
+  uint8_t quality_report_id;
+  // Packet type of the connection.
+  uint8_t packet_types;
+  // Connection handle of the connection.
+  uint16_t connection_handle;
+  // Performing Role for the connection.
+  uint8_t connection_role;
+  // Current Transmit Power Level for the connection. This value is the same as
+  // the controller's response to the HCI_Read_Transmit_Power_Level HCI command.
+  int8_t tx_power_level;
+  // Received Signal Strength Indication (RSSI) value for the connection. This
+  // value is an absolute receiver signal strength value.
+  int8_t rssi;
+  // Signal-to-Noise Ratio (SNR) value for the connection. It is the average
+  // SNR of all the channels used by the link currently.
+  uint8_t snr;
+  // Indicates the number of unused channels in AFH_channel_map.
+  uint8_t unused_afh_channel_count;
+  // Indicates the number of the channels which are interfered and quality is
+  // bad but are still selected for AFH.
+  uint8_t afh_select_unideal_channel_count;
+  // Current Link Supervision Timeout Setting.
+  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
+  uint16_t lsto;
+  // Piconet Clock for the specified Connection_Handle. This value is the same
+  // as the controller's response to HCI_Read_Clock HCI command with the
+  // parameter "Which_Clock" of 0x01 (Piconet Clock).
+  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
+  uint32_t connection_piconet_clock;
+  // The count of retransmission.
+  uint32_t retransmission_count;
+  // The count of no RX.
+  uint32_t no_rx_count;
+  // The count of NAK (Negative Acknowledge).
+  uint32_t nak_count;
+  // Timestamp of last TX ACK.
+  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
+  uint32_t last_tx_ack_timestamp;
+  // The count of Flow-off (STOP).
+  uint32_t flow_off_count;
+  // Timestamp of last Flow-on (GO).
+  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
+  uint32_t last_flow_on_timestamp;
+  // Buffer overflow count (how many bytes of TX data are dropped) since the
+  // last event.
+  uint32_t buffer_overflow_bytes;
+  // Buffer underflow count (in byte).
+  uint32_t buffer_underflow_bytes;
+  // For the controller vendor to obtain more vendor specific parameters.
+  uint8_t* vendor_specific_parameter;
+} BqrLinkQualityEvent;
+
+// Log dump related BQR event
+typedef struct {
+  // Quality report ID.
+  uint8_t quality_report_id;
+  // Connection handle of the connection.
+  uint16_t connection_handle;
+  // For the controller vendor to obtain more vendor specific parameters.
+  uint8_t* vendor_specific_parameter;
+} BqrLogDumpEvent;
+
 // BQR sub-event of Vendor Specific Event
 class BqrVseSubEvt {
  public:
-  // Parse the Bluetooth Quality Report VSE sub-event.
+  // Parse the Link Quality related BQR event.
   //
   // @param length Total length of all parameters contained in the sub-event.
   // @param p_param_buf A pointer to the parameters contained in the sub-event.
-  // @return false If the parameter total length is abnormal.
-  //         true If all parameters are parsed successfully.
-  bool ParseBqrEvt(uint8_t length, uint8_t* p_param_buf);
-
+  void ParseBqrLinkQualityEvt(uint8_t length, uint8_t* p_param_buf);
+  // Write the LMP/LL message trace to the log file.
+  //
+  // @param fd The File Descriptor of the log file.
+  // @param length Total length of all parameters contained in the sub-event.
+  // @param p_param_buf A pointer to the parameters contained in the sub-event.
+  void WriteLmpLlTraceLogFile(int fd, uint8_t length, uint8_t* p_param_buf);
+  // Write the Bluetooth Multi-profile/Coex scheduling trace to the log file.
+  //
+  // @param fd The File Descriptor of the log file.
+  // @param length Total length of all parameters contained in the sub-event.
+  // @param p_param_buf A pointer to the parameters contained in the sub-event.
+  void WriteBtSchedulingTraceLogFile(int fd, uint8_t length,
+                                     uint8_t* p_param_buf);
   // Get a string representation of the Bluetooth Quality event.
   //
   // @return a string representation of the Bluetooth Quality event.
@@ -153,56 +287,10 @@
   }
 
   virtual ~BqrVseSubEvt() = default;
-
-  // Quality report ID.
-  uint8_t quality_report_id_ = 0;
-  // Packet type of the connection.
-  uint8_t packet_types_ = 0;
-  // Connection handle of the connection.
-  uint16_t connection_handle_ = 0;
-  // Performing Role for the connection.
-  uint8_t connection_role_ = 0;
-  // Current Transmit Power Level for the connection. This value is the same as
-  // the controller's response to the HCI_Read_Transmit_Power_Level HCI command.
-  uint8_t tx_power_level_ = 0;
-  // Received Signal Strength Indication (RSSI) value for the connection. This
-  // value is an absolute receiver signal strength value.
-  int8_t rssi_ = 0;
-  // Signal-to-Noise Ratio (SNR) value for the connection. It is the average
-  // SNR of all the channels used by the link currently.
-  uint8_t snr_ = 0;
-  // Indicates the number of unused channels in AFH_channel_map.
-  uint8_t unused_afh_channel_count_ = 0;
-  // Indicates the number of the channels which are interfered and quality is
-  // bad but are still selected for AFH.
-  uint8_t afh_select_unideal_channel_count_ = 0;
-  // Current Link Supervision Timeout Setting.
-  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
-  uint16_t lsto_ = 0;
-  // Piconet Clock for the specified Connection_Handle. This value is the same
-  // as the controller's response to HCI_Read_Clock HCI command with the
-  // parameter "Which_Clock" of 0x01 (Piconet Clock).
-  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
-  uint32_t connection_piconet_clock_ = 0;
-  // The count of retransmission.
-  uint32_t retransmission_count_ = 0;
-  // The count of no RX.
-  uint32_t no_rx_count_ = 0;
-  // The count of NAK (Negative Acknowledge).
-  uint32_t nak_count_ = 0;
-  // Timestamp of last TX ACK.
-  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
-  uint32_t last_tx_ack_timestamp_ = 0;
-  // The count of Flow-off (STOP).
-  uint32_t flow_off_count_ = 0;
-  // Timestamp of last Flow-on (GO).
-  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
-  uint32_t last_flow_on_timestamp_ = 0;
-  // Buffer overflow count (how many bytes of TX data are dropped) since the
-  // last event.
-  uint32_t buffer_overflow_bytes_ = 0;
-  // Buffer underflow count (in byte).
-  uint32_t buffer_underflow_bytes_ = 0;
+  // Link Quality related BQR event
+  BqrLinkQualityEvent bqr_link_quality_event_ = {};
+  // Log Dump related BQR event
+  BqrLogDumpEvent bqr_log_dump_event_ = {};
   // Local wall clock timestamp of receiving BQR VSE sub-event
   std::tm tm_timestamp_ = {};
 };
@@ -230,23 +318,11 @@
 //   mechanism in the Bluetooth controller.
 void EnableBtQualityReport(bool is_enable);
 
-// Dump Bluetooth Quality Report information.
-//
-// @param fd The file descriptor to use for dumping information.
-void DebugDump(int fd);
-
 // Configure Bluetooth Quality Report setting to the Bluetooth controller.
 //
 // @param bqr_config The struct of configuration parameters.
 void ConfigureBqr(const BqrConfiguration& bqr_config);
 
-// Invoked on completion of Bluetooth Quality Report configuration. Then it will
-// Register/Unregister for receiving VSE - Bluetooth Quality Report sub event.
-//
-// @param current_evt_mask Indicates current quality event bit mask setting in
-//   the Bluetooth controller.
-void ConfigureBqrCmpl(uint32_t current_evt_mask);
-
 // Callback invoked on completion of vendor specific Bluetooth Quality Report
 // command.
 //
@@ -254,13 +330,56 @@
 //   specific command complete event.
 void BqrVscCompleteCallback(tBTM_VSC_CMPL* p_vsc_cmpl_params);
 
-// Record a new incoming Bluetooth Quality Report in quality event queue.
+// Invoked on completion of Bluetooth Quality Report configuration. Then it will
+// Register/Unregister for receiving VSE - Bluetooth Quality Report sub-event.
 //
-// @param len Lengths of the quality report sent from the Bluetooth
+// @param current_evt_mask Indicates current quality event bit mask setting in
+//   the Bluetooth controller.
+void ConfigureBqrCmpl(uint32_t current_evt_mask);
+
+// Categorize the incoming Bluetooth Quality Report.
+//
+// @param length Lengths of the quality report sent from the Bluetooth
 //   controller.
-// @param p_quality_report A pointer to the quality report which is sent from
-//   the Bluetooth controller via Vendor Specific Event.
-void AddBqrEventToQueue(uint8_t length, uint8_t* p_stream);
+// @param p_bqr_event A pointer to the BQR VSE sub-event which is sent from the
+//   Bluetooth controller.
+void CategorizeBqrEvent(uint8_t length, uint8_t* p_bqr_event);
+
+// Record a new incoming Link Quality related BQR event in quality event queue.
+//
+// @param length Lengths of the Link Quality related BQR event.
+// @param p_link_quality_event A pointer to the Link Quality related BQR event.
+void AddLinkQualityEventToQueue(uint8_t length, uint8_t* p_link_quality_event);
+
+// Dump the LMP/LL message handshaking with the remote device to a log file.
+//
+// @param length Lengths of the LMP/LL message trace event.
+// @param p_lmp_ll_message_event A pointer to the LMP/LL message trace event.
+void DumpLmpLlMessage(uint8_t length, uint8_t* p_lmp_ll_message_event);
+
+// Open the LMP/LL message trace log file.
+//
+// @return a file descriptor of the LMP/LL message trace log file.
+int OpenLmpLlTraceLogFile();
+
+// Dump the Bluetooth Multi-profile/Coex scheduling information to a log file.
+//
+// @param length Lengths of the Bluetooth Multi-profile/Coex scheduling trace
+//   event.
+// @param p_bt_scheduling_event A pointer to the Bluetooth Multi-profile/Coex
+//   scheduling trace event.
+void DumpBtScheduling(uint8_t length, uint8_t* p_bt_scheduling_event);
+
+// Open the Bluetooth Multi-profile/Coex scheduling trace log file.
+//
+// @return a file descriptor of the Bluetooth Multi-profile/Coex scheduling
+//   trace log file.
+int OpenBtSchedulingTraceLogFile();
+
+// Dump Bluetooth Quality Report information.
+//
+// @param fd The file descriptor to use for dumping information.
+void DebugDump(int fd);
 
 }  // namespace bqr
 }  // namespace bluetooth
diff --git a/btif/include/btif_common.h b/btif/include/btif_common.h
index 39034c3..98f8304 100644
--- a/btif/include/btif_common.h
+++ b/btif/include/btif_common.h
@@ -28,7 +28,7 @@
 #include <hardware/bluetooth.h>
 
 #include "bt_types.h"
-#include "bta_api.h"
+#include "bta/include/bta_api.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 
@@ -36,12 +36,12 @@
  *  Constants & Macros
  ******************************************************************************/
 
-#define ASSERTC(cond, msg, val)                                              \
-  do {                                                                       \
-    if (!(cond)) {                                                           \
-      LOG_ERROR(LOG_TAG, "### ASSERT : %s %s line %d %s (%d) ###", __FILE__, \
-                __func__, __LINE__, (msg), (val));                           \
-    }                                                                        \
+#define ASSERTC(cond, msg, val)                                               \
+  do {                                                                        \
+    if (!(cond)) {                                                            \
+      LOG_ERROR("### ASSERT : %s %s line %d %s (%d) ###", __FILE__, __func__, \
+                __LINE__, (msg), (val));                                      \
+    }                                                                         \
   } while (0)
 
 /* Calculate start of event enumeration; id is top 8 bits of event */
diff --git a/btif/include/btif_config.h b/btif/include/btif_config.h
index 028afa7..3297725 100644
--- a/btif/include/btif_config.h
+++ b/btif/include/btif_config.h
@@ -29,7 +29,18 @@
 
 static const char BTIF_CONFIG_MODULE[] = "btif_config_module";
 
-bool btif_config_has_section(const char* section);
+static const std::string BT_CONFIG_KEY_SDP_DI_MANUFACTURER =
+    "SdpDiManufacturer";
+static const std::string BT_CONFIG_KEY_SDP_DI_MODEL = "SdpDiModel";
+static const std::string BT_CONFIG_KEY_SDP_DI_HW_VERSION =
+    "SdpDiHardwareVersion";
+static const std::string BT_CONFIG_KEY_SDP_DI_VENDOR_ID_SRC =
+    "SdpDiVendorIdSource";
+
+static const std::string BT_CONFIG_KEY_REMOTE_VER_MFCT = "Manufacturer";
+static const std::string BT_CONFIG_KEY_REMOTE_VER_VER = "LmpVer";
+static const std::string BT_CONFIG_KEY_REMOTE_VER_SUBVER = "LmpSubVer";
+
 bool btif_config_exist(const std::string& section, const std::string& key);
 bool btif_config_get_int(const std::string& section, const std::string& key,
                          int* value);
@@ -52,7 +63,7 @@
 size_t btif_config_get_bin_length(const std::string& section,
                                   const std::string& key);
 
-std::list<section_t>& btif_config_sections();
+std::vector<RawAddress> btif_config_get_paired_devices();
 
 void btif_config_save(void);
 void btif_config_flush(void);
diff --git a/btif/include/btif_config_cache.h b/btif/include/btif_config_cache.h
new file mode 100644
index 0000000..61406f9
--- /dev/null
+++ b/btif/include/btif_config_cache.h
@@ -0,0 +1,60 @@
+/*
+ *  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.
+ */
+
+#pragma once
+
+#include <map>
+#include <unordered_set>
+
+#include "common/lru.h"
+#include "osi/include/config.h"
+#include "osi/include/log.h"
+#include "raw_address.h"
+
+class BtifConfigCache {
+ public:
+  explicit BtifConfigCache(size_t capacity);
+  ~BtifConfigCache();
+
+  void Clear();
+  void Init(std::unique_ptr<config_t> source);
+  std::vector<std::string> GetPersistentSectionNames();
+  config_t PersistentSectionCopy();
+  bool HasSection(const std::string& section_name);
+  bool HasUnpairedSection(const std::string& section_name);
+  bool HasPersistentSection(const std::string& section_name);
+  bool HasKey(const std::string& section_name, const std::string& key);
+  bool RemoveKey(const std::string& section_name, const std::string& key);
+  void RemovePersistentSectionsWithKey(const std::string& key);
+
+  // Setters and getters
+  void SetString(std::string section_name, std::string key, std::string value);
+  std::optional<std::string> GetString(const std::string& section_name,
+                                       const std::string& key);
+  void SetInt(std::string section_name, std::string key, int value);
+  std::optional<int> GetInt(const std::string& section_name,
+                            const std::string& key);
+  void SetUint64(std::string section_name, std::string key, uint64_t value);
+  std::optional<uint64_t> GetUint64(const std::string& section_name,
+                                    const std::string& key);
+  void SetBool(std::string section_name, std::string key, bool value);
+  std::optional<bool> GetBool(const std::string& section_name,
+                              const std::string& key);
+
+ private:
+  bluetooth::common::LruCache<std::string, section_t> unpaired_devices_cache_;
+  config_t paired_devices_list_;
+};
diff --git a/btif/include/btif_dm.h b/btif/include/btif_dm.h
index a9d4cb6..63ff0e7 100644
--- a/btif/include/btif_dm.h
+++ b/btif/include/btif_dm.h
@@ -19,7 +19,7 @@
 #ifndef BTIF_DM_H
 #define BTIF_DM_H
 
-#include "bta_api.h"
+#include "bta/include/bta_api.h"
 #include "bte_appl.h"
 #include "btif_uid.h"
 
diff --git a/btif/include/btif_keystore.h b/btif/include/btif_keystore.h
index 4762350..460f3b8 100644
--- a/btif/include/btif_keystore.h
+++ b/btif/include/btif_keystore.h
@@ -1,70 +1,25 @@
-/******************************************************************************
+/*
+ * Copyright 2020 The Android Open Source Project
  *
- *  Copyright 2019 Google, Inc.
+ * 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
  *
- *  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
  *
- *  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.
- *
- ******************************************************************************/
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
-#include <base/logging.h>
-#include <keystore/keystore_client_impl.h>
-#include <mutex>
-
-#include "osi/include/alarm.h"
-#include "osi/include/allocator.h"
-#include "osi/include/compat.h"
-#include "osi/include/config.h"
-#include "osi/include/log.h"
-#include "osi/include/osi.h"
-#include "osi/include/properties.h"
+#include <hardware/bt_keystore.h>
 
 namespace bluetooth {
-/**
- * Client wrapper to access AndroidKeystore.
- *
- * <p>Use to encrypt/decrypt data and store to disk.
- */
-class BtifKeystore {
- public:
-  /**
-   * @param keystore_client injected pre-created client object for keystore
-   */
-  BtifKeystore(keystore::KeystoreClient* keystore_client);
+namespace bluetooth_keystore {
 
-  /**
-   * Encrypts given data
-   *
-   * <p>Returns a string representation of the encrypted data
-   *
-   * @param data to be encrypted
-   * @param flags for keystore
-   */
-  std::string Encrypt(const std::string& data, int32_t flags);
+BluetoothKeystoreInterface* getBluetoothKeystoreInterface();
 
-  /**
-   * Returns a decrypted string representation of the encrypted data or empty
-   * string on error.
-   *
-   * @param input encrypted data
-   */
-  std::string Decrypt(const std::string& input_filename);
-
- private:
-  std::unique_ptr<keystore::KeystoreClient> keystore_client_;
-  std::mutex api_mutex_;
-  keystore::KeyStoreNativeReturnCode GenerateKey(const std::string& name,
-                                                 int32_t flags,
-                                                 bool auth_bound);
-};
-
-}  // namespace bluetooth
+}  // namespace bluetooth_keystore
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/btif/include/btif_storage.h b/btif/include/btif_storage.h
index 1c1163d..a1e52d9 100644
--- a/btif/include/btif_storage.h
+++ b/btif/include/btif_storage.h
@@ -219,7 +219,7 @@
  *                  BT_STATUS_FAIL otherwise
  *
  ******************************************************************************/
-bt_status_t btif_storage_remove_hid_info(RawAddress* remote_bd_addr);
+bt_status_t btif_storage_remove_hid_info(const RawAddress& remote_bd_addr);
 
 /** Loads information about bonded hearing aid devices */
 void btif_storage_load_bonded_hearing_aids();
@@ -231,6 +231,13 @@
 void btif_storage_set_hearing_aid_white_list(const RawAddress& address,
                                              bool add_to_whitelist);
 
+/** Stores information about GATT Client supported features support */
+void btif_storage_set_gatt_cl_supp_feat(const RawAddress& bd_addr,
+                                        uint8_t feat);
+
+/** Get client supported features */
+uint8_t btif_storage_get_gatt_cl_supp_feat(const RawAddress& bd_addr);
+
 /** Get the hearing aid device properties. */
 bool btif_storage_get_hearing_aid_prop(
     const RawAddress& address, uint8_t* capabilities, uint64_t* hi_sync_id,
@@ -255,7 +262,7 @@
                                              const uint8_t* key,
                                              uint8_t key_type,
                                              uint8_t key_length);
-bt_status_t btif_storage_get_ble_bonding_key(RawAddress* remote_bd_addr,
+bt_status_t btif_storage_get_ble_bonding_key(const RawAddress& remote_bd_addr,
                                              uint8_t key_type,
                                              uint8_t* key_value,
                                              int key_length);
@@ -294,7 +301,7 @@
  *
  ******************************************************************************/
 
-bt_status_t btif_storage_set_hidd(RawAddress* remote_bd_addr);
+bt_status_t btif_storage_set_hidd(const RawAddress& remote_bd_addr);
 
 /*******************************************************************************
  *
diff --git a/btif/src/bluetooth.cc b/btif/src/bluetooth.cc
index ae13843..761c9fd 100644
--- a/btif/src/bluetooth.cc
+++ b/btif/src/bluetooth.cc
@@ -59,12 +59,16 @@
 #include "btif_debug_btsnoop.h"
 #include "btif_debug_conn.h"
 #include "btif_hf.h"
+#include "btif_keystore.h"
 #include "btif_storage.h"
 #include "btsnoop.h"
 #include "btsnoop_mem.h"
 #include "common/address_obfuscator.h"
+#include "common/metric_id_allocator.h"
 #include "common/metrics.h"
 #include "device/include/interop.h"
+#include "gd/common/init_flags.h"
+#include "main/shim/dumpsys.h"
 #include "main/shim/shim.h"
 #include "osi/include/alarm.h"
 #include "osi/include/allocation_tracker.h"
@@ -82,7 +86,9 @@
 
 bt_callbacks_t* bt_hal_cbacks = NULL;
 bool restricted_mode = false;
-bool single_user_mode = false;
+bool niap_mode = false;
+const int CONFIG_COMPARE_ALL_PASS = 0b11;
+int niap_config_compare_result = CONFIG_COMPARE_ALL_PASS;
 
 /*******************************************************************************
  *  Externs
@@ -135,15 +141,12 @@
  ****************************************************************************/
 
 static int init(bt_callbacks_t* callbacks, bool start_restricted,
-                bool is_single_user_mode) {
-  LOG_INFO(LOG_TAG, "%s: start restricted = %d ; single user = %d", __func__,
-           start_restricted, is_single_user_mode);
+                bool is_niap_mode, int config_compare_result,
+                const char** init_flags) {
+  LOG_INFO("%s: start restricted = %d ; niap = %d, config compare result = %d",
+           __func__, start_restricted, is_niap_mode, config_compare_result);
 
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    LOG_INFO(LOG_TAG, "%s Enable Gd bluetooth functionality", __func__);
-  } else {
-    LOG_INFO(LOG_TAG, "%s Preserving legacy bluetooth functionality", __func__);
-  }
+  bluetooth::common::InitFlags::Load(init_flags);
 
   if (interface_ready()) return BT_STATUS_DONE;
 
@@ -153,7 +156,9 @@
 
   bt_hal_cbacks = callbacks;
   restricted_mode = start_restricted;
-  single_user_mode = is_single_user_mode;
+  niap_mode = is_niap_mode;
+  niap_config_compare_result = config_compare_result;
+
   stack_manager_get_interface()->init_stack();
   btif_debug_init();
   return BT_STATUS_SUCCESS;
@@ -176,7 +181,12 @@
 static void cleanup(void) { stack_manager_get_interface()->clean_up_stack(); }
 
 bool is_restricted_mode() { return restricted_mode; }
-bool is_single_user_mode() { return single_user_mode; }
+bool is_niap_mode() { return niap_mode; }
+// if niap mode disable, will always return CONFIG_COMPARE_ALL_PASS(0b11)
+// indicate don't check config checksum.
+int get_niap_config_compare_result() {
+  return niap_mode ? niap_config_compare_result : CONFIG_COMPARE_ALL_PASS;
+}
 
 static int get_adapter_properties(void) {
   /* sanity check */
@@ -328,9 +338,13 @@
   HearingAid::DebugDump(fd);
   connection_manager::dump(fd);
   bluetooth::bqr::DebugDump(fd);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    bluetooth::shim::Dump(fd, arguments);
+  } else {
 #if (BTSNOOP_MEM == TRUE)
-  btif_debug_btsnoop_dump(fd);
+    btif_debug_btsnoop_dump(fd);
 #endif
+  }
 }
 
 static void dumpMetrics(std::string* output) {
@@ -338,7 +352,7 @@
 }
 
 static const void* get_profile_interface(const char* profile_id) {
-  LOG_INFO(LOG_TAG, "%s: id = %s", __func__, profile_id);
+  LOG_INFO("%s: id = %s", __func__, profile_id);
 
   /* sanity check */
   if (!interface_ready()) return NULL;
@@ -382,11 +396,14 @@
 
   if (is_profile(profile_id, BT_PROFILE_HEARING_AID_ID))
     return btif_hearing_aid_get_interface();
+
+  if (is_profile(profile_id, BT_KEYSTORE_ID))
+    return bluetooth::bluetooth_keystore::getBluetoothKeystoreInterface();
   return NULL;
 }
 
 int dut_mode_configure(uint8_t enable) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
 
   /* sanity check */
   if (!interface_ready()) return BT_STATUS_NOT_READY;
@@ -395,7 +412,7 @@
 }
 
 int dut_mode_send(uint16_t opcode, uint8_t* buf, uint8_t len) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
 
   /* sanity check */
   if (!interface_ready()) return BT_STATUS_NOT_READY;
@@ -404,7 +421,7 @@
 }
 
 int le_test_mode(uint16_t opcode, uint8_t* buf, uint8_t len) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
 
   /* sanity check */
   if (!interface_ready()) return BT_STATUS_NOT_READY;
@@ -442,7 +459,7 @@
 }
 
 static int config_clear(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   return btif_config_clear() ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
 }
 
@@ -455,6 +472,11 @@
       address);
 }
 
+static int get_metric_id(const RawAddress& address) {
+  return bluetooth::common::MetricIdAllocator::GetInstance().AllocateId(
+      address);
+}
+
 EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
     sizeof(bluetoothInterface),
     init,
@@ -491,4 +513,5 @@
     interop_database_add,
     get_avrcp_service,
     obfuscate_address,
+    get_metric_id,
 };
diff --git a/btif/src/btif_a2dp.cc b/btif/src/btif_a2dp.cc
index e2d6132..c79d567 100644
--- a/btif/src/btif_a2dp.cc
+++ b/btif/src/btif_a2dp.cc
@@ -37,7 +37,7 @@
 #include "osi/include/log.h"
 
 void btif_a2dp_on_idle(void) {
-  LOG_INFO(LOG_TAG, "%s: ## ON A2DP IDLE ## peer_sep = %d", __func__,
+  LOG_INFO("%s: ## ON A2DP IDLE ## peer_sep = %d", __func__,
            btif_av_get_peer_sep());
   if (btif_av_get_peer_sep() == AVDT_TSEP_SNK) {
     btif_a2dp_source_on_idle();
@@ -60,6 +60,7 @@
     if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
       bluetooth::audio::a2dp::ack_stream_started(status);
     } else if (btif_av_is_a2dp_offload_enabled()) {
+      // TODO: BluetoothA2dp@1.0 is deprecated
       btif_a2dp_audio_on_started(status);
     } else {
       btif_a2dp_command_ack(status);
@@ -75,7 +76,7 @@
       LOG(WARNING) << __func__ << ": peer " << peer_addr << " A2DP is suspending and ignores the started event";
       return false;
     }
-    if (btif_av_is_a2dp_offload_enabled()) {
+    if (btif_av_is_a2dp_offload_running()) {
       btif_av_stream_start_offload();
     } else if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
       if (btif_av_get_peer_sep() == AVDT_TSEP_SNK) {
@@ -98,6 +99,7 @@
     if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
       bluetooth::audio::a2dp::ack_stream_started(A2DP_CTRL_ACK_FAILURE);
     } else if (btif_av_is_a2dp_offload_enabled()) {
+      // TODO: BluetoothA2dp@1.0 is deprecated
       btif_a2dp_audio_on_started(p_av_start->status);
     } else {
       btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
@@ -108,32 +110,33 @@
 }
 
 void btif_a2dp_on_stopped(tBTA_AV_SUSPEND* p_av_suspend) {
-  LOG_INFO(LOG_TAG, "%s: ## ON A2DP STOPPED ## p_av_suspend=%p", __func__,
-           p_av_suspend);
+  LOG_INFO("%s: ## ON A2DP STOPPED ## p_av_suspend=%p", __func__, p_av_suspend);
 
   if (btif_av_get_peer_sep() == AVDT_TSEP_SRC) {
     btif_a2dp_sink_on_stopped(p_av_suspend);
     return;
   }
   if (bluetooth::audio::a2dp::is_hal_2_0_enabled() ||
-      !btif_av_is_a2dp_offload_enabled()) {
+      !btif_av_is_a2dp_offload_running()) {
     btif_a2dp_source_on_stopped(p_av_suspend);
   } else if (p_av_suspend != NULL) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_on_stopped(p_av_suspend->status);
   }
 }
 
 void btif_a2dp_on_suspended(tBTA_AV_SUSPEND* p_av_suspend) {
-  LOG_INFO(LOG_TAG, "%s: ## ON A2DP SUSPENDED ## p_av_suspend=%p", __func__,
+  LOG_INFO("%s: ## ON A2DP SUSPENDED ## p_av_suspend=%p", __func__,
            p_av_suspend);
   if (btif_av_get_peer_sep() == AVDT_TSEP_SRC) {
     btif_a2dp_sink_on_suspended(p_av_suspend);
     return;
   }
   if (bluetooth::audio::a2dp::is_hal_2_0_enabled() ||
-      !btif_av_is_a2dp_offload_enabled()) {
+      !btif_av_is_a2dp_offload_running()) {
     btif_a2dp_source_on_suspended(p_av_suspend);
   } else if (p_av_suspend != NULL) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_on_suspended(p_av_suspend->status);
   }
 }
@@ -141,30 +144,30 @@
 void btif_a2dp_on_offload_started(const RawAddress& peer_addr,
                                   tBTA_AV_STATUS status) {
   tA2DP_CTRL_ACK ack;
-  LOG_INFO(LOG_TAG, "%s: peer %s status %d", __func__,
-           peer_addr.ToString().c_str(), status);
+  LOG_INFO("%s: peer %s status %d", __func__, peer_addr.ToString().c_str(),
+           status);
 
   switch (status) {
     case BTA_AV_SUCCESS:
       ack = A2DP_CTRL_ACK_SUCCESS;
       break;
     case BTA_AV_FAIL_RESOURCES:
-      LOG_ERROR(LOG_TAG, "%s: peer %s FAILED UNSUPPORTED", __func__,
+      LOG_ERROR("%s: peer %s FAILED UNSUPPORTED", __func__,
                 peer_addr.ToString().c_str());
       ack = A2DP_CTRL_ACK_UNSUPPORTED;
       break;
     default:
-      LOG_ERROR(LOG_TAG, "%s: peer %s FAILED: status = %d", __func__,
+      LOG_ERROR("%s: peer %s FAILED: status = %d", __func__,
                 peer_addr.ToString().c_str(), status);
       ack = A2DP_CTRL_ACK_FAILURE;
       break;
   }
-  if (btif_av_is_a2dp_offload_enabled()) {
+  if (btif_av_is_a2dp_offload_running()) {
     if (ack != BTA_AV_SUCCESS && btif_av_stream_started_ready()) {
       // Offload request will return with failure from btif_av sm if
       // suspend is triggered for remote start. Disconnect only if SoC
       // returned failure for offload VSC
-      LOG_ERROR(LOG_TAG, "%s: peer %s offload start failed", __func__,
+      LOG_ERROR("%s: peer %s offload start failed", __func__,
                 peer_addr.ToString().c_str());
       btif_av_src_disconnect_sink(peer_addr);
     }
@@ -173,6 +176,7 @@
     bluetooth::audio::a2dp::ack_stream_started(ack);
   } else {
     btif_a2dp_command_ack(ack);
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_on_started(status);
   }
 }
diff --git a/btif/src/btif_a2dp_audio_interface.cc b/btif/src/btif_a2dp_audio_interface.cc
index 026baa1..b304693 100644
--- a/btif/src/btif_a2dp_audio_interface.cc
+++ b/btif/src/btif_a2dp_audio_interface.cc
@@ -152,7 +152,7 @@
 
   // TODO : Delay reporting
   /*    Return<void> a2dp_get_sink_latency() {
-          LOG_INFO(LOG_TAG,"%s:start ", __func__);
+          LOG_INFO("%s:start ", __func__);
           btif_a2dp_audio_send_sink_latency();
           return Void();
       }*/
@@ -163,7 +163,7 @@
   void serviceDied(
       uint64_t /*cookie*/,
       const wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
-    LOG_ERROR(LOG_TAG, "%s", __func__);
+    LOG_ERROR("%s", __func__);
     // Restart the session on the correct thread
     do_in_main_thread(FROM_HERE,
                       base::Bind(&btif_a2dp_audio_interface_restart_session));
@@ -193,7 +193,7 @@
 
 static void btif_a2dp_get_codec_configuration(
     CodecConfiguration* p_codec_info) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   tBT_A2DP_OFFLOAD a2dp_offload;
   A2dpCodecConfig* a2dpCodecConfig = bta_av_get_a2dp_current_codec();
   a2dpCodecConfig->getCodecSpecificConfig(&a2dp_offload);
@@ -207,7 +207,7 @@
               BTA_AV_CODEC_TYPE_SBC;
       p_codec_info->codecSpecific.sbcData.codecParameters =
           a2dp_offload.codec_info[0];
-      LOG_INFO(LOG_TAG, " %s: codec parameters =%d", __func__,
+      LOG_INFO(" %s: codec parameters =%d", __func__,
                a2dp_offload.codec_info[0]);
       p_codec_info->codecSpecific.sbcData.minBitpool =
           a2dp_offload.codec_info[1];
@@ -251,8 +251,8 @@
   } else {
     p_codec_info->peerMtu = peer_param.peer_mtu;
   }
-  LOG_INFO(LOG_TAG, "%s: peer MTU: %d effective MTU: %d result MTU: %d",
-           __func__, peer_param.peer_mtu, effectiveMtu, p_codec_info->peerMtu);
+  LOG_INFO("%s: peer MTU: %d effective MTU: %d result MTU: %d", __func__,
+           peer_param.peer_mtu, effectiveMtu, p_codec_info->peerMtu);
 
   p_codec_info->sampleRate =
       (::android::hardware::bluetooth::a2dp::V1_0::SampleRate)
@@ -267,31 +267,29 @@
 }
 
 static void btif_a2dp_audio_interface_init() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
 
   btAudio = IBluetoothAudioOffload::getService();
   CHECK(btAudio != nullptr);
 
   auto death_link = btAudio->linkToDeath(bluetoothAudioDeathRecipient, 0);
   if (!death_link.isOk()) {
-    LOG_ERROR(LOG_TAG, "%s: Cannot observe the Bluetooth Audio HAL's death",
-              __func__);
+    LOG_ERROR("%s: Cannot observe the Bluetooth Audio HAL's death", __func__);
   }
 
-  LOG_DEBUG(
-      LOG_TAG, "%s: IBluetoothAudioOffload::getService() returned %p (%s)",
-      __func__, btAudio.get(), (btAudio->isRemote() ? "remote" : "local"));
+  LOG_DEBUG("%s: IBluetoothAudioOffload::getService() returned %p (%s)",
+            __func__, btAudio.get(),
+            (btAudio->isRemote() ? "remote" : "local"));
 
-  LOG_INFO(LOG_TAG, "%s:Init returned", __func__);
+  LOG_INFO("%s:Init returned", __func__);
 }
 
 static void btif_a2dp_audio_interface_deinit() {
-  LOG_INFO(LOG_TAG, "%s: start", __func__);
+  LOG_INFO("%s: start", __func__);
   if (btAudio != nullptr) {
     auto death_unlink = btAudio->unlinkToDeath(bluetoothAudioDeathRecipient);
     if (!death_unlink.isOk()) {
-      LOG_ERROR(LOG_TAG,
-                "%s: Error unlinking death observer from Bluetooth Audio HAL",
+      LOG_ERROR("%s: Error unlinking death observer from Bluetooth Audio HAL",
                 __func__);
     }
   }
@@ -299,7 +297,7 @@
 }
 
 void btif_a2dp_audio_interface_start_session() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
       bluetooth::common::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
   a2dp_offload_audio_stats.Reset();
@@ -312,7 +310,7 @@
 }
 
 void btif_a2dp_audio_interface_end_session() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   a2dp_offload_audio_stats.LogAudioStopMetricsAndReset();
   BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
       bluetooth::common::DISCONNECT_REASON_UNKNOWN, 0);
@@ -320,17 +318,16 @@
   if (btAudio == nullptr) return;
   auto ret = btAudio->endSession();
   if (!ret.isOk()) {
-    LOG_ERROR(LOG_TAG, "HAL server is dead");
+    LOG_ERROR("HAL server is dead");
   }
   btif_a2dp_audio_interface_deinit();
 }
 
 // Conditionally restart the session only if it was started before
 static void btif_a2dp_audio_interface_restart_session() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   if (btAudio == nullptr) {
-    LOG_INFO(LOG_TAG, "%s: nothing to restart - session was not started",
-             __func__);
+    LOG_INFO("%s: nothing to restart - session was not started", __func__);
     return;
   }
   btAudio = nullptr;
@@ -338,13 +335,13 @@
 }
 
 void btif_a2dp_audio_on_started(tBTA_AV_STATUS status) {
-  LOG_INFO(LOG_TAG, "%s: status = %d", __func__, status);
+  LOG_INFO("%s: status = %d", __func__, status);
   if (btAudio != nullptr) {
     if (a2dp_cmd_pending == A2DP_CTRL_CMD_START) {
       if (status != A2DP_CTRL_ACK_PENDING) {
         a2dp_cmd_pending = A2DP_CTRL_CMD_NONE;
       }
-      LOG_INFO(LOG_TAG, "%s: calling method onStarted", __func__);
+      LOG_INFO("%s: calling method onStarted", __func__);
       auto hal_status = mapToStatus(status);
       btAudio->streamStarted(hal_status);
       if (hal_status == Status::SUCCESS) {
@@ -355,13 +352,13 @@
 }
 
 void btif_a2dp_audio_on_suspended(tBTA_AV_STATUS status) {
-  LOG_INFO(LOG_TAG, "%s: status = %d", __func__, status);
+  LOG_INFO("%s: status = %d", __func__, status);
   if (btAudio != nullptr) {
     if (a2dp_cmd_pending == A2DP_CTRL_CMD_SUSPEND) {
       if (status != A2DP_CTRL_ACK_PENDING) {
         a2dp_cmd_pending = A2DP_CTRL_CMD_NONE;
       }
-      LOG_INFO(LOG_TAG, "calling method onSuspended");
+      LOG_INFO("calling method onSuspended");
       auto hal_status = mapToStatus(status);
       btAudio->streamSuspended(hal_status);
       if (hal_status == Status::SUCCESS) {
@@ -372,17 +369,16 @@
 }
 
 void btif_a2dp_audio_on_stopped(tBTA_AV_STATUS status) {
-  LOG_INFO(LOG_TAG, "%s: status = %d", __func__, status);
+  LOG_INFO("%s: status = %d", __func__, status);
   if (btAudio != nullptr && a2dp_cmd_pending == A2DP_CTRL_CMD_START) {
     a2dp_cmd_pending = A2DP_CTRL_CMD_NONE;
-    LOG_INFO(LOG_TAG, "%s: Remote disconnected when start under progress",
-             __func__);
+    LOG_INFO("%s: Remote disconnected when start under progress", __func__);
     btAudio->streamStarted(mapToStatus(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS));
     a2dp_offload_audio_stats.LogAudioStopMetricsAndReset();
   }
 }
 void btif_a2dp_audio_send_start_req() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   uint8_t resp;
   resp = btif_a2dp_audio_process_request(A2DP_CTRL_CMD_START);
   if (btAudio != nullptr) {
@@ -391,11 +387,11 @@
     if (status == Status::SUCCESS) {
       a2dp_offload_audio_stats.LogAudioStart();
     }
-    if (!ret.isOk()) LOG_ERROR(LOG_TAG, "HAL server died");
+    if (!ret.isOk()) LOG_ERROR("HAL server died");
   }
 }
 void btif_a2dp_audio_send_suspend_req() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   uint8_t resp;
   resp = btif_a2dp_audio_process_request(A2DP_CTRL_CMD_SUSPEND);
   if (btAudio != nullptr) {
@@ -404,28 +400,28 @@
     if (status == Status::SUCCESS) {
       a2dp_offload_audio_stats.LogAudioStopMetricsAndReset();
     }
-    if (!ret.isOk()) LOG_ERROR(LOG_TAG, "HAL server died");
+    if (!ret.isOk()) LOG_ERROR("HAL server died");
   }
 }
 
 void btif_a2dp_audio_send_stop_req() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   btif_a2dp_audio_process_request(A2DP_CTRL_CMD_STOP);
   a2dp_offload_audio_stats.LogAudioStopMetricsAndReset();
 }
 
 /*void btif_a2dp_audio_send_sink_latency()
 {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   uint16_t sink_latency = btif_av_get_sink_latency();
   if (btAudio != nullptr) {
     auto ret = btAudio->a2dp_on_get_sink_latency(sink_latency);
-    if (!ret.isOk()) LOG_ERROR(LOG_TAG, "server died");
+    if (!ret.isOk()) LOG_ERROR("server died");
   }
 }*/
 
 uint8_t btif_a2dp_audio_process_request(uint8_t cmd) {
-  LOG_INFO(LOG_TAG, "%s: cmd: %s", __func__,
+  LOG_INFO("%s: cmd: %s", __func__,
            audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd));
   uint8_t status;
   switch (cmd) {
@@ -508,7 +504,7 @@
       status = A2DP_CTRL_ACK_FAILURE;
       break;
   }
-  LOG_INFO(LOG_TAG, "a2dp-ctrl-cmd : %s DONE returning status %d",
+  LOG_INFO("a2dp-ctrl-cmd : %s DONE returning status %d",
            audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd), status);
   if (status == A2DP_CTRL_ACK_PENDING) {
     a2dp_cmd_pending = cmd;
diff --git a/btif/src/btif_a2dp_sink.cc b/btif/src/btif_a2dp_sink.cc
index af845a7..4ffac7a 100644
--- a/btif/src/btif_a2dp_sink.cc
+++ b/btif/src/btif_a2dp_sink.cc
@@ -64,7 +64,9 @@
   BTIF_MEDIA_SINK_DECODER_UPDATE = 1,
   BTIF_MEDIA_SINK_CLEAR_TRACK,
   BTIF_MEDIA_SINK_SET_FOCUS_STATE,
-  BTIF_MEDIA_SINK_AUDIO_RX_FLUSH
+  BTIF_MEDIA_SINK_AUDIO_RX_FLUSH,
+  BTIF_MEDIA_SINK_START,
+  BTIF_MEDIA_SINK_SUSPEND
 };
 
 typedef struct {
@@ -149,6 +151,8 @@
     btif_a2dp_sink_focus_state_t state);
 static void btif_a2dp_sink_audio_rx_flush_event();
 static void btif_a2dp_sink_clear_track_event_req();
+static void btif_a2dp_sink_on_start_event();
+static void btif_a2dp_sink_on_suspend_event();
 
 UNUSED_ATTR static const char* dump_media_event(uint16_t event) {
   switch (event) {
@@ -156,6 +160,8 @@
     CASE_RETURN_STR(BTIF_MEDIA_SINK_CLEAR_TRACK)
     CASE_RETURN_STR(BTIF_MEDIA_SINK_SET_FOCUS_STATE)
     CASE_RETURN_STR(BTIF_MEDIA_SINK_AUDIO_RX_FLUSH)
+    CASE_RETURN_STR(BTIF_MEDIA_SINK_START)
+    CASE_RETURN_STR(BTIF_MEDIA_SINK_SUSPEND)
     default:
       break;
   }
@@ -163,11 +169,11 @@
 }
 
 bool btif_a2dp_sink_init() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   LockGuard lock(g_mutex);
 
   if (btif_a2dp_sink_state != BTIF_A2DP_SINK_STATE_OFF) {
-    LOG_ERROR(LOG_TAG, "%s: A2DP Sink media task already running", __func__);
+    LOG_ERROR("%s: A2DP Sink media task already running", __func__);
     return false;
   }
 
@@ -177,7 +183,7 @@
   /* Start A2DP Sink media task */
   btif_a2dp_sink_cb.worker_thread.StartUp();
   if (!btif_a2dp_sink_cb.worker_thread.IsRunning()) {
-    LOG_ERROR(LOG_TAG, "%s: unable to start up media thread", __func__);
+    LOG_ERROR("%s: unable to start up media thread", __func__);
     btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_OFF;
     return false;
   }
@@ -195,19 +201,19 @@
 }
 
 static void btif_a2dp_sink_init_delayed() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_RUNNING;
 }
 
 bool btif_a2dp_sink_startup() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   btif_a2dp_sink_cb.worker_thread.DoInThread(
       FROM_HERE, base::BindOnce(btif_a2dp_sink_startup_delayed));
   return true;
 }
 
 static void btif_a2dp_sink_startup_delayed() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   LockGuard lock(g_mutex);
   // Nothing to do
 }
@@ -264,33 +270,32 @@
 }
 
 bool btif_a2dp_sink_end_session(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__,
-           peer_address.ToString().c_str());
+  LOG_INFO("%s: peer_address=%s", __func__, peer_address.ToString().c_str());
   btif_a2dp_sink_cb.worker_thread.DoInThread(
       FROM_HERE, base::BindOnce(btif_a2dp_sink_end_session_delayed));
   return true;
 }
 
 static void btif_a2dp_sink_end_session_delayed() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   LockGuard lock(g_mutex);
   // Nothing to do
 }
 
 void btif_a2dp_sink_shutdown() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   btif_a2dp_sink_cb.worker_thread.DoInThread(
       FROM_HERE, base::BindOnce(btif_a2dp_sink_shutdown_delayed));
 }
 
 static void btif_a2dp_sink_shutdown_delayed() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   LockGuard lock(g_mutex);
   // Nothing to do
 }
 
 void btif_a2dp_sink_cleanup() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
 
   alarm_t* decode_alarm;
 
@@ -320,7 +325,7 @@
 }
 
 static void btif_a2dp_sink_cleanup_delayed() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   LockGuard lock(g_mutex);
 
   fixed_queue_free(btif_a2dp_sink_cb.rx_audio_queue, nullptr);
@@ -344,7 +349,7 @@
 }
 
 static void btif_a2dp_sink_command_ready(BT_HDR* p_msg) {
-  LOG_VERBOSE(LOG_TAG, "%s: event %d %s", __func__, p_msg->event,
+  LOG_VERBOSE("%s: event %d %s", __func__, p_msg->event,
               dump_media_event(p_msg->event));
 
   switch (p_msg->event) {
@@ -364,17 +369,23 @@
     case BTIF_MEDIA_SINK_AUDIO_RX_FLUSH:
       btif_a2dp_sink_audio_rx_flush_event();
       break;
+    case BTIF_MEDIA_SINK_START:
+      btif_a2dp_sink_on_start_event();
+      break;
+    case BTIF_MEDIA_SINK_SUSPEND:
+      btif_a2dp_sink_on_suspend_event();
+      break;
     default:
-      LOG_ERROR(LOG_TAG, "%s: unknown event %d", __func__, p_msg->event);
+      LOG_ERROR("%s: unknown event %d", __func__, p_msg->event);
       break;
   }
 
   osi_free(p_msg);
-  LOG_VERBOSE(LOG_TAG, "%s: %s DONE", __func__, dump_media_event(p_msg->event));
+  LOG_VERBOSE("%s: %s DONE", __func__, dump_media_event(p_msg->event));
 }
 
 void btif_a2dp_sink_update_decoder(const uint8_t* p_codec_info) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   tBTIF_MEDIA_SINK_DECODER_UPDATE* p_buf =
       reinterpret_cast<tBTIF_MEDIA_SINK_DECODER_UPDATE*>(
           osi_malloc(sizeof(tBTIF_MEDIA_SINK_DECODER_UPDATE)));
@@ -391,26 +402,52 @@
 }
 
 void btif_a2dp_sink_on_idle() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
+  BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR)));
+  p_buf->event = BTIF_MEDIA_SINK_SUSPEND;
+  btif_a2dp_sink_cb.worker_thread.DoInThread(
+      FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
+
   if (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) return;
   btif_a2dp_sink_audio_handle_stop_decoding();
   btif_a2dp_sink_clear_track_event_req();
 }
 
 void btif_a2dp_sink_on_stopped(UNUSED_ATTR tBTA_AV_SUSPEND* p_av_suspend) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
+  BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR)));
+  p_buf->event = BTIF_MEDIA_SINK_SUSPEND;
+  btif_a2dp_sink_cb.worker_thread.DoInThread(
+      FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
+
   if (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) return;
   btif_a2dp_sink_audio_handle_stop_decoding();
 }
 
 void btif_a2dp_sink_on_suspended(UNUSED_ATTR tBTA_AV_SUSPEND* p_av_suspend) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
+  BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR)));
+  p_buf->event = BTIF_MEDIA_SINK_SUSPEND;
+  btif_a2dp_sink_cb.worker_thread.DoInThread(
+      FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
+
   if (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) return;
   btif_a2dp_sink_audio_handle_stop_decoding();
 }
 
+bool btif_a2dp_sink_on_start() {
+  LOG_INFO("%s", __func__);
+
+  BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR)));
+  p_buf->event = BTIF_MEDIA_SINK_START;
+  btif_a2dp_sink_cb.worker_thread.DoInThread(
+      FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
+
+  return true;
+}
+
 static void btif_a2dp_sink_audio_handle_stop_decoding() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   alarm_t* old_alarm;
   {
     LockGuard lock(g_mutex);
@@ -441,7 +478,7 @@
 }
 
 static void btif_a2dp_sink_clear_track_event() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   LockGuard lock(g_mutex);
 
 #ifndef OS_GENERIC
@@ -453,7 +490,7 @@
 
 // Must be called while locked.
 static void btif_a2dp_sink_audio_handle_start_decoding() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   if (btif_a2dp_sink_cb.decode_alarm != nullptr)
     return;  // Already started decoding
 
@@ -463,7 +500,7 @@
 
   btif_a2dp_sink_cb.decode_alarm = alarm_new_periodic("btif.a2dp_sink_decode");
   if (btif_a2dp_sink_cb.decode_alarm == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s: unable to allocate decode alarm", __func__);
+    LOG_ERROR("%s: unable to allocate decode alarm", __func__);
     return;
   }
   alarm_set(btif_a2dp_sink_cb.decode_alarm, BTIF_SINK_MEDIA_TIME_TICK_MS,
@@ -487,7 +524,7 @@
 
   CHECK(btif_a2dp_sink_cb.decoder_interface != nullptr);
   if (!btif_a2dp_sink_cb.decoder_interface->decode_packet(p_msg)) {
-    LOG_ERROR(LOG_TAG, "%s: decoding failed", __func__);
+    LOG_ERROR("%s: decoding failed", __func__);
   }
 }
 
@@ -530,14 +567,14 @@
 
 /* when true media task discards any rx frames */
 void btif_a2dp_sink_set_rx_flush(bool enable) {
-  LOG_INFO(LOG_TAG, "%s: enable=%s", __func__, (enable) ? "true" : "false");
+  LOG_INFO("%s: enable=%s", __func__, (enable) ? "true" : "false");
   LockGuard lock(g_mutex);
 
   btif_a2dp_sink_cb.rx_flush = enable;
 }
 
 static void btif_a2dp_sink_audio_rx_flush_event() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   LockGuard lock(g_mutex);
   // Flush all received encoded audio buffers
   fixed_queue_flush(btif_a2dp_sink_cb.rx_audio_queue, osi_free);
@@ -545,7 +582,7 @@
 
 static void btif_a2dp_sink_decoder_update_event(
     tBTIF_MEDIA_SINK_DECODER_UPDATE* p_buf) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   LockGuard lock(g_mutex);
   APPL_TRACE_DEBUG("%s: p_codec_info[%x:%x:%x:%x:%x:%x]", __func__,
                    p_buf->codec_info[1], p_buf->codec_info[2],
@@ -554,22 +591,22 @@
 
   int sample_rate = A2DP_GetTrackSampleRate(p_buf->codec_info);
   if (sample_rate == -1) {
-    LOG_ERROR(LOG_TAG, "%s: cannot get the track frequency", __func__);
+    LOG_ERROR("%s: cannot get the track frequency", __func__);
     return;
   }
   int bits_per_sample = A2DP_GetTrackBitsPerSample(p_buf->codec_info);
   if (bits_per_sample == -1) {
-    LOG_ERROR(LOG_TAG, "%s: cannot get the bits per sample", __func__);
+    LOG_ERROR("%s: cannot get the bits per sample", __func__);
     return;
   }
   int channel_count = A2DP_GetTrackChannelCount(p_buf->codec_info);
   if (channel_count == -1) {
-    LOG_ERROR(LOG_TAG, "%s: cannot get the channel count", __func__);
+    LOG_ERROR("%s: cannot get the channel count", __func__);
     return;
   }
   int channel_type = A2DP_GetSinkTrackChannelType(p_buf->codec_info);
   if (channel_type == -1) {
-    LOG_ERROR(LOG_TAG, "%s: cannot get the Sink channel type", __func__);
+    LOG_ERROR("%s: cannot get the Sink channel type", __func__);
     return;
   }
   btif_a2dp_sink_cb.sample_rate = sample_rate;
@@ -581,17 +618,20 @@
 
   btif_a2dp_sink_cb.decoder_interface = bta_av_co_get_decoder_interface();
   if (btif_a2dp_sink_cb.decoder_interface == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s: cannot stream audio: no source decoder interface",
-              __func__);
+    LOG_ERROR("%s: cannot stream audio: no source decoder interface", __func__);
     return;
   }
 
   if (!btif_a2dp_sink_cb.decoder_interface->decoder_init(
           btif_a2dp_sink_on_decode_complete)) {
-    LOG_ERROR(LOG_TAG, "%s: failed to initialize decoder", __func__);
+    LOG_ERROR("%s: failed to initialize decoder", __func__);
     return;
   }
 
+  if (btif_a2dp_sink_cb.decoder_interface->decoder_configure != nullptr) {
+    btif_a2dp_sink_cb.decoder_interface->decoder_configure(p_buf->codec_info);
+  }
+
   APPL_TRACE_DEBUG("%s: create audio track", __func__);
   btif_a2dp_sink_cb.audio_track =
 #ifndef OS_GENERIC
@@ -600,7 +640,7 @@
       NULL;
 #endif
   if (btif_a2dp_sink_cb.audio_track == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s: track creation failed", __func__);
+    LOG_ERROR("%s: track creation failed", __func__);
     return;
   }
 }
@@ -635,7 +675,7 @@
 }
 
 void btif_a2dp_sink_audio_rx_flush_req() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   if (fixed_queue_is_empty(btif_a2dp_sink_cb.rx_audio_queue)) {
     /* Queue is already empty */
     return;
@@ -652,7 +692,7 @@
 }
 
 void btif_a2dp_sink_set_focus_state_req(btif_a2dp_sink_focus_state_t state) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   tBTIF_MEDIA_SINK_FOCUS_UPDATE* p_buf =
       reinterpret_cast<tBTIF_MEDIA_SINK_FOCUS_UPDATE*>(
           osi_malloc(sizeof(tBTIF_MEDIA_SINK_FOCUS_UPDATE)));
@@ -664,7 +704,7 @@
 
 static void btif_a2dp_sink_set_focus_state_event(
     btif_a2dp_sink_focus_state_t state) {
-  LOG_INFO(LOG_TAG, "%s: state=%d", __func__, state);
+  LOG_INFO("%s: state=%d", __func__, state);
   LockGuard lock(g_mutex);
 
   APPL_TRACE_DEBUG("%s: setting focus state to %d", __func__, state);
@@ -678,7 +718,7 @@
 }
 
 void btif_a2dp_sink_set_audio_track_gain(float gain) {
-  LOG_INFO(LOG_TAG, "%s: set gain to %f", __func__, gain);
+  LOG_INFO("%s: set gain to %f", __func__, gain);
   LockGuard lock(g_mutex);
 
 #ifndef OS_GENERIC
@@ -687,10 +727,32 @@
 }
 
 static void btif_a2dp_sink_clear_track_event_req() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR)));
 
   p_buf->event = BTIF_MEDIA_SINK_CLEAR_TRACK;
   btif_a2dp_sink_cb.worker_thread.DoInThread(
       FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
 }
+
+static void btif_a2dp_sink_on_start_event() {
+  LOG_INFO("%s", __func__);
+
+  if ((btif_a2dp_sink_cb.decoder_interface != nullptr) &&
+      (btif_a2dp_sink_cb.decoder_interface->decoder_start != nullptr)) {
+    btif_a2dp_sink_cb.decoder_interface->decoder_start();
+  }
+
+  return;
+}
+
+static void btif_a2dp_sink_on_suspend_event() {
+  LOG_INFO("%s", __func__);
+
+  if ((btif_a2dp_sink_cb.decoder_interface != nullptr) &&
+      (btif_a2dp_sink_cb.decoder_interface->decoder_suspend != nullptr)) {
+    btif_a2dp_sink_cb.decoder_interface->decoder_suspend();
+  }
+
+  return;
+}
diff --git a/btif/src/btif_a2dp_source.cc b/btif/src/btif_a2dp_source.cc
index 9951ce1..45b2e3f 100644
--- a/btif/src/btif_a2dp_source.cc
+++ b/btif/src/btif_a2dp_source.cc
@@ -246,7 +246,8 @@
     const RawAddress& peer_address);
 static void btif_a2dp_source_encoder_user_config_update_event(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config);
+    const std::vector<btav_a2dp_codec_config_t>& codec_user_preferences,
+    std::promise<void> peer_ready_promise);
 static void btif_a2dp_source_audio_feeding_update_event(
     const btav_a2dp_codec_config_t& codec_audio_config);
 static bool btif_a2dp_source_audio_tx_flush_req(void);
@@ -316,7 +317,7 @@
 }
 
 bool btif_a2dp_source_init(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
 
   // Start A2DP Source media task
   btif_a2dp_source_thread.StartUp();
@@ -326,16 +327,15 @@
 }
 
 static void btif_a2dp_source_init_delayed(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   // Nothing to do
 }
 
 bool btif_a2dp_source_startup(void) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   if (btif_a2dp_source_cb.State() != BtifA2dpSource::kStateOff) {
-    LOG_ERROR(LOG_TAG, "%s: A2DP Source media task already running", __func__);
+    LOG_ERROR("%s: A2DP Source media task already running", __func__);
     return false;
   }
 
@@ -351,13 +351,13 @@
 }
 
 static void btif_a2dp_source_startup_delayed() {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
   if (!btif_a2dp_source_thread.EnableRealTimeScheduling()) {
     LOG(FATAL) << __func__ << ": unable to enable real time scheduling";
   }
   if (!bluetooth::audio::a2dp::init(&btif_a2dp_source_thread)) {
     if (btif_av_is_a2dp_offload_enabled()) {
+      // TODO: BluetoothA2dp@1.0 is deprecated
       LOG(WARNING) << __func__ << ": Using BluetoothA2dp HAL";
     } else {
       LOG(WARNING) << __func__ << ": Using legacy HAL";
@@ -397,9 +397,11 @@
   }
   if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
     bluetooth::audio::a2dp::start_session();
+    bluetooth::audio::a2dp::set_remote_delay(btif_av_get_audio_delay());
     BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
         bluetooth::common::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
   } else if (btif_av_is_a2dp_offload_enabled()) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_interface_start_session();
   } else {
     BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
@@ -433,17 +435,15 @@
   }
 
   // Start the session.
-  // If audio was streaming before, start audio streaming as well.
   btif_a2dp_source_start_session(new_peer_address,
                                  std::move(peer_ready_promise));
-  if (is_streaming) {
-    btif_a2dp_source_start_audio_req();
-  }
+  // If audio was streaming before, DON'T start audio streaming, but leave the
+  // control to the audio HAL.
   return true;
 }
 
 bool btif_a2dp_source_end_session(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+  LOG_INFO("%s: peer_address=%s state=%s", __func__,
            peer_address.ToString().c_str(),
            btif_a2dp_source_cb.StateStr().c_str());
   btif_a2dp_source_thread.DoInThread(
@@ -454,20 +454,21 @@
 
 static void btif_a2dp_source_end_session_delayed(
     const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+  LOG_INFO("%s: peer_address=%s state=%s", __func__,
            peer_address.ToString().c_str(),
            btif_a2dp_source_cb.StateStr().c_str());
   if ((btif_a2dp_source_cb.State() == BtifA2dpSource::kStateRunning) ||
       (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateShuttingDown)) {
     btif_av_stream_stop(peer_address);
   } else {
-    LOG_ERROR(LOG_TAG, "%s: A2DP Source media task is not running", __func__);
+    LOG_ERROR("%s: A2DP Source media task is not running", __func__);
   }
   if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
     bluetooth::audio::a2dp::end_session();
     BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
         bluetooth::common::DISCONNECT_REASON_UNKNOWN, 0);
   } else if (btif_av_is_a2dp_offload_enabled()) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_interface_end_session();
   } else {
     BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
@@ -476,8 +477,7 @@
 }
 
 void btif_a2dp_source_shutdown(void) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   if ((btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) ||
       (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateShuttingDown)) {
@@ -492,8 +492,7 @@
 }
 
 static void btif_a2dp_source_shutdown_delayed(void) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   // Stop the timer
   btif_a2dp_source_cb.media_alarm.CancelAndWait();
@@ -502,6 +501,7 @@
   if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
     bluetooth::audio::a2dp::cleanup();
   } else if (btif_av_is_a2dp_offload_enabled()) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_interface_end_session();
   } else {
     btif_a2dp_control_cleanup();
@@ -513,8 +513,7 @@
 }
 
 void btif_a2dp_source_cleanup(void) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   // Make sure the source is shutdown
   btif_a2dp_source_shutdown();
@@ -527,8 +526,7 @@
 }
 
 static void btif_a2dp_source_cleanup_delayed(void) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
   // Nothing to do
 }
 
@@ -545,7 +543,7 @@
 }
 
 static void btif_a2dp_source_setup_codec(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+  LOG_INFO("%s: peer_address=%s state=%s", __func__,
            peer_address.ToString().c_str(),
            btif_a2dp_source_cb.StateStr().c_str());
 
@@ -561,7 +559,7 @@
 
 static void btif_a2dp_source_setup_codec_delayed(
     const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+  LOG_INFO("%s: peer_address=%s state=%s", __func__,
            peer_address.ToString().c_str(),
            btif_a2dp_source_cb.StateStr().c_str());
 
@@ -569,21 +567,19 @@
   bta_av_co_get_peer_params(peer_address, &peer_params);
 
   if (!bta_av_co_set_active_peer(peer_address)) {
-    LOG_ERROR(LOG_TAG, "%s: Cannot stream audio: cannot set active peer to %s",
-              __func__, peer_address.ToString().c_str());
+    LOG_ERROR("%s: Cannot stream audio: cannot set active peer to %s", __func__,
+              peer_address.ToString().c_str());
     return;
   }
   btif_a2dp_source_cb.encoder_interface = bta_av_co_get_encoder_interface();
   if (btif_a2dp_source_cb.encoder_interface == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s: Cannot stream audio: no source encoder interface",
-              __func__);
+    LOG_ERROR("%s: Cannot stream audio: no source encoder interface", __func__);
     return;
   }
 
   A2dpCodecConfig* a2dp_codec_config = bta_av_get_a2dp_current_codec();
   if (a2dp_codec_config == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s: Cannot stream audio: current codec is not set",
-              __func__);
+    LOG_ERROR("%s: Cannot stream audio: current codec is not set", __func__);
     return;
   }
 
@@ -601,16 +597,14 @@
 }
 
 void btif_a2dp_source_start_audio_req(void) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_audio_tx_start_event));
 }
 
 void btif_a2dp_source_stop_audio_req(void) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_audio_tx_stop_event));
@@ -618,30 +612,63 @@
 
 void btif_a2dp_source_encoder_user_config_update_req(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
-           peer_address.ToString().c_str(),
-           btif_a2dp_source_cb.StateStr().c_str());
-  btif_a2dp_source_thread.DoInThread(
-      FROM_HERE, base::Bind(&btif_a2dp_source_encoder_user_config_update_event,
-                            peer_address, codec_user_config));
+    const std::vector<btav_a2dp_codec_config_t>& codec_user_preferences,
+    std::promise<void> peer_ready_promise) {
+  LOG(INFO) << __func__ << ": peer_address=" << peer_address
+            << " state=" << btif_a2dp_source_cb.StateStr() << " "
+            << codec_user_preferences.size() << " codec_preference(s)";
+  if (!btif_a2dp_source_thread.DoInThread(
+          FROM_HERE,
+          base::BindOnce(&btif_a2dp_source_encoder_user_config_update_event,
+                         peer_address, codec_user_preferences,
+                         std::move(peer_ready_promise)))) {
+    // cannot set promise but triggers crash
+    LOG(FATAL) << __func__ << ": peer_address=" << peer_address
+               << " state=" << btif_a2dp_source_cb.StateStr()
+               << " fails to context switch";
+  }
 }
 
 static void btif_a2dp_source_encoder_user_config_update_event(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
-           peer_address.ToString().c_str(),
-           btif_a2dp_source_cb.StateStr().c_str());
-  if (!bta_av_co_set_codec_user_config(peer_address, codec_user_config)) {
-    LOG_ERROR(LOG_TAG, "%s: cannot update codec user configuration", __func__);
+    const std::vector<btav_a2dp_codec_config_t>& codec_user_preferences,
+    std::promise<void> peer_ready_promise) {
+  bool restart_output = false;
+  bool success = false;
+  for (auto codec_user_config : codec_user_preferences) {
+    success = bta_av_co_set_codec_user_config(peer_address, codec_user_config,
+                                              &restart_output);
+    if (success) {
+      LOG(INFO) << __func__ << ": peer_address=" << peer_address
+                << " state=" << btif_a2dp_source_cb.StateStr()
+                << " codec_preference={" << codec_user_config.ToString()
+                << "} restart_output=" << (restart_output ? "true" : "false");
+      break;
+    }
+  }
+  if (success && restart_output) {
+    // Codec reconfiguration is in progress, and it is safe to unlock since
+    // remaining tasks like starting audio session and reporting new codec
+    // will be handled by BTA_AV_RECONFIG_EVT later.
+    peer_ready_promise.set_value();
+    return;
+  }
+  if (!success) {
+    LOG(ERROR) << __func__ << ": cannot update codec user configuration(s)";
+  }
+  if (!peer_address.IsEmpty() && peer_address == btif_av_source_active_peer()) {
+    // No more actions needed with remote, and if succeed, user had changed the
+    // config like the bits per sample only. Let's resume the session now.
+    btif_a2dp_source_start_session(peer_address, std::move(peer_ready_promise));
+  } else {
+    // Unlock for non-active peer
+    peer_ready_promise.set_value();
   }
 }
 
 void btif_a2dp_source_feeding_update_req(
     const btav_a2dp_codec_config_t& codec_audio_config) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_audio_feeding_update_event,
                             codec_audio_config));
@@ -649,17 +676,14 @@
 
 static void btif_a2dp_source_audio_feeding_update_event(
     const btav_a2dp_codec_config_t& codec_audio_config) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
   if (!bta_av_co_set_codec_audio_config(codec_audio_config)) {
-    LOG_ERROR(LOG_TAG, "%s: cannot update codec audio feeding parameters",
-              __func__);
+    LOG_ERROR("%s: cannot update codec audio feeding parameters", __func__);
   }
 }
 
 void btif_a2dp_source_on_idle(void) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
   if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
   /* Make sure media task is stopped */
@@ -667,88 +691,87 @@
 }
 
 void btif_a2dp_source_on_stopped(tBTA_AV_SUSPEND* p_av_suspend) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
-  /* allow using this api for other than suspend */
-  if (p_av_suspend != nullptr) {
-    if (p_av_suspend->status != BTA_AV_SUCCESS) {
-      LOG_ERROR(LOG_TAG, "%s: A2DP stop request failed: status=%d", __func__,
-                p_av_suspend->status);
-      if (p_av_suspend->initiator) {
-        LOG_WARN(LOG_TAG, "%s: A2DP stop request failed: status=%d", __func__,
-                 p_av_suspend->status);
-        if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
-          bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_FAILURE);
-        } else {
-          btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
-        }
-      }
-      return;
-    }
-  }
-  if (btif_av_is_a2dp_offload_enabled()) {
-    bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS);
-    return;
-  }
-  /* ensure tx frames are immediately suspended */
-  btif_a2dp_source_cb.tx_flush = true;
-
-  /* request to stop media task */
-  btif_a2dp_source_audio_tx_flush_req();
-  btif_a2dp_source_stop_audio_req();
-
-  /* once stream is fully stopped we will ack back */
-}
-
-void btif_a2dp_source_on_suspended(tBTA_AV_SUSPEND* p_av_suspend) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
-
-  if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
-
-  /* check for status failures */
-  if (p_av_suspend->status != BTA_AV_SUCCESS) {
+  // allow using this API for other (acknowledgement and stopping media task)
+  // than suspend
+  if (p_av_suspend != nullptr && p_av_suspend->status != BTA_AV_SUCCESS) {
+    LOG_ERROR("%s: A2DP stop failed: status=%d, initiator=%s", __func__,
+              p_av_suspend->status,
+              (p_av_suspend->initiator ? "true" : "false"));
     if (p_av_suspend->initiator) {
-      LOG_WARN(LOG_TAG, "%s: A2DP suspend request failed: status=%d", __func__,
-               p_av_suspend->status);
       if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
         bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_FAILURE);
       } else {
         btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
       }
     }
-  }
-  if (btif_av_is_a2dp_offload_enabled()) {
+  } else if (btif_av_is_a2dp_offload_running()) {
     bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS);
     return;
   }
-  /* once stream is fully stopped we will ack back */
 
-  /* ensure tx frames are immediately flushed */
+  // ensure tx frames are immediately suspended
+  btif_a2dp_source_cb.tx_flush = true;
+  // ensure tx frames are immediately flushed
+  btif_a2dp_source_audio_tx_flush_req();
+
+  // request to stop media task
+  btif_a2dp_source_stop_audio_req();
+
+  // once software stream is fully stopped we will ack back
+}
+
+void btif_a2dp_source_on_suspended(tBTA_AV_SUSPEND* p_av_suspend) {
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
+
+  if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
+
+  CHECK(p_av_suspend != nullptr) << "Suspend result could not be nullptr";
+
+  // check for status failures
+  if (p_av_suspend->status != BTA_AV_SUCCESS) {
+    LOG_WARN("%s: A2DP suspend failed: status=%d, initiator=%s", __func__,
+             p_av_suspend->status,
+             (p_av_suspend->initiator ? "true" : "false"));
+    if (p_av_suspend->initiator) {
+      if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
+        bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_FAILURE);
+      } else {
+        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
+      }
+    }
+  } else if (btif_av_is_a2dp_offload_running()) {
+    bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS);
+    return;
+  }
+
+  // ensure tx frames are immediately suspended
   btif_a2dp_source_cb.tx_flush = true;
 
-  /* stop timer tick */
+  // stop timer tick
   btif_a2dp_source_stop_audio_req();
+
+  // once software stream is fully stopped we will ack back
 }
 
 /* when true media task discards any tx frames */
 void btif_a2dp_source_set_tx_flush(bool enable) {
-  LOG_INFO(LOG_TAG, "%s: enable=%s state=%s", __func__,
-           (enable) ? "true" : "false", btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: enable=%s state=%s", __func__, (enable) ? "true" : "false",
+           btif_a2dp_source_cb.StateStr().c_str());
   btif_a2dp_source_cb.tx_flush = enable;
 }
 
 static void btif_a2dp_source_audio_tx_start_event(void) {
-  LOG_INFO(LOG_TAG, "%s: media_alarm is %srunning, streaming %s state=%s",
-           __func__,
-           btif_a2dp_source_cb.media_alarm.IsScheduled() ? "" : "not ",
-           btif_a2dp_source_is_streaming() ? "true" : "false",
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO(
+      "%s: media_alarm is %s, streaming %s state=%s", __func__,
+      btif_a2dp_source_cb.media_alarm.IsScheduled() ? "running" : "stopped",
+      btif_a2dp_source_is_streaming() ? "true" : "false",
+      btif_a2dp_source_cb.StateStr().c_str());
 
-  if (btif_av_is_a2dp_offload_enabled()) return;
+  if (btif_av_is_a2dp_offload_running()) return;
 
   /* Reset the media feeding state */
   CHECK(btif_a2dp_source_cb.encoder_interface != nullptr);
@@ -758,6 +781,9 @@
       "%s: starting timer %" PRIu64 " ms", __func__,
       btif_a2dp_source_cb.encoder_interface->get_encoder_interval_ms());
 
+  /* audio engine starting, reset tx suspended flag */
+  btif_a2dp_source_cb.tx_flush = false;
+
   wakelock_acquire();
   btif_a2dp_source_cb.media_alarm.SchedulePeriodic(
       btif_a2dp_source_thread.GetWeakPtr(), FROM_HERE,
@@ -782,13 +808,13 @@
 }
 
 static void btif_a2dp_source_audio_tx_stop_event(void) {
-  LOG_INFO(LOG_TAG, "%s: media_alarm is %srunning, streaming %s state=%s",
-           __func__,
-           btif_a2dp_source_cb.media_alarm.IsScheduled() ? "" : "not ",
-           btif_a2dp_source_is_streaming() ? "true" : "false",
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO(
+      "%s: media_alarm is %s, streaming %s state=%s", __func__,
+      btif_a2dp_source_cb.media_alarm.IsScheduled() ? "running" : "stopped",
+      btif_a2dp_source_is_streaming() ? "true" : "false",
+      btif_a2dp_source_cb.StateStr().c_str());
 
-  if (btif_av_is_a2dp_offload_enabled()) return;
+  if (btif_av_is_a2dp_offload_running()) return;
 
   btif_a2dp_source_cb.stats.session_end_us =
       bluetooth::common::time_get_os_boottime_us();
@@ -818,7 +844,7 @@
     UIPC_Close(*a2dp_uipc, UIPC_CH_ID_AV_AUDIO);
 
     /*
-     * Try to send acknowldegment once the media stream is
+     * Try to send acknowledgement once the media stream is
      * stopped. This will make sure that the A2DP HAL layer is
      * un-blocked on wait for acknowledgment for the sent command.
      * This resolves a corner cases AVDTP SUSPEND collision
@@ -842,14 +868,13 @@
 }
 
 static void btif_a2dp_source_audio_handle_timer(void) {
-  if (btif_av_is_a2dp_offload_enabled()) return;
+  if (btif_av_is_a2dp_offload_running()) return;
 
   uint64_t timestamp_us = bluetooth::common::time_get_os_boottime_us();
   log_tstamps_us("A2DP Source tx timer", timestamp_us);
 
   if (!btif_a2dp_source_cb.media_alarm.IsScheduled()) {
-    LOG_ERROR(LOG_TAG, "%s: ERROR Media task Scheduled after Suspend",
-              __func__);
+    LOG_ERROR("%s: ERROR Media task Scheduled after Suspend", __func__);
     return;
   }
   CHECK(btif_a2dp_source_cb.encoder_interface != nullptr);
@@ -881,7 +906,7 @@
   }
 
   if (bytes_read < len) {
-    LOG_WARN(LOG_TAG, "%s: UNDERFLOW: ONLY READ %d BYTES OUT OF %d", __func__,
+    LOG_WARN("%s: UNDERFLOW: ONLY READ %d BYTES OUT OF %d", __func__,
              bytes_read, len);
     btif_a2dp_source_cb.stats.media_read_total_underflow_bytes +=
         (len - bytes_read);
@@ -909,7 +934,7 @@
 
   /* Check if the transmission queue has been flushed */
   if (btif_a2dp_source_cb.tx_flush) {
-    LOG_VERBOSE(LOG_TAG, "%s: tx suspended, discarded frame", __func__);
+    LOG_VERBOSE("%s: tx suspended, discarded frame", __func__);
 
     btif_a2dp_source_cb.stats.tx_queue_total_flushed_messages +=
         fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue);
@@ -924,8 +949,7 @@
   // TODO: Using frames_n here is probably wrong: should be "+ 1" instead.
   if (fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue) + frames_n >
       MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ) {
-    LOG_WARN(LOG_TAG, "%s: TX queue buffer size now=%u adding=%u max=%d",
-             __func__,
+    LOG_WARN("%s: TX queue buffer size now=%u adding=%u max=%d", __func__,
              (uint32_t)fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue),
              (uint32_t)frames_n, MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ);
     // Keep track of drop-outs
@@ -958,25 +982,24 @@
     RawAddress peer_bda = btif_av_source_active_peer();
     tBTM_STATUS status = BTM_ReadRSSI(peer_bda, btm_read_rssi_cb);
     if (status != BTM_CMD_STARTED) {
-      LOG_WARN(LOG_TAG, "%s: Cannot read RSSI: status %d", __func__, status);
+      LOG_WARN("%s: Cannot read RSSI: status %d", __func__, status);
     }
     status = BTM_ReadFailedContactCounter(peer_bda,
                                           btm_read_failed_contact_counter_cb);
     if (status != BTM_CMD_STARTED) {
-      LOG_WARN(LOG_TAG, "%s: Cannot read Failed Contact Counter: status %d",
-               __func__, status);
+      LOG_WARN("%s: Cannot read Failed Contact Counter: status %d", __func__,
+               status);
     }
     status = BTM_ReadAutomaticFlushTimeout(peer_bda,
                                            btm_read_automatic_flush_timeout_cb);
     if (status != BTM_CMD_STARTED) {
-      LOG_WARN(LOG_TAG, "%s: Cannot read Automatic Flush Timeout: status %d",
-               __func__, status);
+      LOG_WARN("%s: Cannot read Automatic Flush Timeout: status %d", __func__,
+               status);
     }
     status =
         BTM_ReadTxPower(peer_bda, BT_TRANSPORT_BR_EDR, btm_read_tx_power_cb);
     if (status != BTM_CMD_STARTED) {
-      LOG_WARN(LOG_TAG, "%s: Cannot read Tx Power: status %d", __func__,
-               status);
+      LOG_WARN("%s: Cannot read Tx Power: status %d", __func__, status);
     }
   }
 
@@ -993,9 +1016,8 @@
 
 static void btif_a2dp_source_audio_tx_flush_event(void) {
   /* Flush all enqueued audio buffers (encoded) */
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
-  if (btif_av_is_a2dp_offload_enabled()) return;
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
+  if (btif_av_is_a2dp_offload_running()) return;
 
   if (btif_a2dp_source_cb.encoder_interface != nullptr)
     btif_a2dp_source_cb.encoder_interface->feeding_flush();
@@ -1012,8 +1034,7 @@
 }
 
 static bool btif_a2dp_source_audio_tx_flush_req(void) {
-  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_audio_tx_flush_event));
@@ -1255,7 +1276,7 @@
   SchedulingStats enqueue_stats = stats.tx_queue_enqueue_stats;
   A2dpSessionMetrics metrics;
   metrics.codec_index = stats.codec_index;
-  metrics.is_a2dp_offload = btif_av_is_a2dp_offload_enabled();
+  metrics.is_a2dp_offload = btif_av_is_a2dp_offload_running();
   // session_start_us is 0 when btif_a2dp_source_start_audio_req() is not called
   // mark the metric duration as invalid (-1) in this case
   if (stats.session_start_us != 0) {
@@ -1299,13 +1320,13 @@
 
 static void btm_read_rssi_cb(void* data) {
   if (data == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s: Read RSSI request timed out", __func__);
+    LOG_ERROR("%s: Read RSSI request timed out", __func__);
     return;
   }
 
   tBTM_RSSI_RESULT* result = (tBTM_RSSI_RESULT*)data;
   if (result->status != BTM_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: unable to read remote RSSI (status %d)", __func__,
+    LOG_ERROR("%s: unable to read remote RSSI (status %d)", __func__,
               result->status);
     return;
   }
@@ -1313,60 +1334,58 @@
       result->rem_bda, bluetooth::common::kUnknownConnectionHandle,
       result->hci_status, result->rssi);
 
-  LOG_WARN(LOG_TAG, "%s: device: %s, rssi: %d", __func__,
+  LOG_WARN("%s: device: %s, rssi: %d", __func__,
            result->rem_bda.ToString().c_str(), result->rssi);
 }
 
 static void btm_read_failed_contact_counter_cb(void* data) {
   if (data == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s: Read Failed Contact Counter request timed out",
-              __func__);
+    LOG_ERROR("%s: Read Failed Contact Counter request timed out", __func__);
     return;
   }
 
   tBTM_FAILED_CONTACT_COUNTER_RESULT* result =
       (tBTM_FAILED_CONTACT_COUNTER_RESULT*)data;
   if (result->status != BTM_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: unable to read Failed Contact Counter (status %d)",
-              __func__, result->status);
+    LOG_ERROR("%s: unable to read Failed Contact Counter (status %d)", __func__,
+              result->status);
     return;
   }
   bluetooth::common::LogReadFailedContactCounterResult(
       result->rem_bda, bluetooth::common::kUnknownConnectionHandle,
       result->hci_status, result->failed_contact_counter);
 
-  LOG_WARN(LOG_TAG, "%s: device: %s, Failed Contact Counter: %u", __func__,
+  LOG_WARN("%s: device: %s, Failed Contact Counter: %u", __func__,
            result->rem_bda.ToString().c_str(), result->failed_contact_counter);
 }
 
 static void btm_read_automatic_flush_timeout_cb(void* data) {
   if (data == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s: Read Automatic Flush Timeout request timed out",
-              __func__);
+    LOG_ERROR("%s: Read Automatic Flush Timeout request timed out", __func__);
     return;
   }
 
   tBTM_AUTOMATIC_FLUSH_TIMEOUT_RESULT* result =
       (tBTM_AUTOMATIC_FLUSH_TIMEOUT_RESULT*)data;
   if (result->status != BTM_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: unable to read Automatic Flush Timeout (status %d)",
+    LOG_ERROR("%s: unable to read Automatic Flush Timeout (status %d)",
               __func__, result->status);
     return;
   }
 
-  LOG_WARN(LOG_TAG, "%s: device: %s, Automatic Flush Timeout: %u", __func__,
+  LOG_WARN("%s: device: %s, Automatic Flush Timeout: %u", __func__,
            result->rem_bda.ToString().c_str(), result->automatic_flush_timeout);
 }
 
 static void btm_read_tx_power_cb(void* data) {
   if (data == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s: Read Tx Power request timed out", __func__);
+    LOG_ERROR("%s: Read Tx Power request timed out", __func__);
     return;
   }
 
   tBTM_TX_POWER_RESULT* result = (tBTM_TX_POWER_RESULT*)data;
   if (result->status != BTM_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: unable to read Tx Power (status %d)", __func__,
+    LOG_ERROR("%s: unable to read Tx Power (status %d)", __func__,
               result->status);
     return;
   }
@@ -1374,6 +1393,6 @@
       result->rem_bda, bluetooth::common::kUnknownConnectionHandle,
       result->hci_status, result->tx_power);
 
-  LOG_WARN(LOG_TAG, "%s: device: %s, Tx Power: %d", __func__,
+  LOG_WARN("%s: device: %s, Tx Power: %d", __func__,
            result->rem_bda.ToString().c_str(), result->tx_power);
 }
diff --git a/btif/src/btif_av.cc b/btif/src/btif_av.cc
index 1795a9b..4f839f4 100644
--- a/btif/src/btif_av.cc
+++ b/btif/src/btif_av.cc
@@ -110,6 +110,7 @@
 };
 
 class BtifAvPeer;
+static bt_status_t sink_set_active_device(const RawAddress& peer_address);
 
 // Should not need dedicated Suspend state as actual actions are no
 // different than Open state. Suspend flags are needed however to prevent
@@ -277,9 +278,18 @@
 
   bool IsConnected() const;
   bool IsStreaming() const;
-  bool IsInSilenceMode() const { return is_silenced_; };
+  bool IsInSilenceMode() const { return is_silenced_; }
 
-  void SetSilence(bool silence) { is_silenced_ = silence; };
+  void SetSilence(bool silence) { is_silenced_ = silence; }
+
+  // AVDTP delay reporting in 1/10 milliseconds
+  void SetDelayReport(uint16_t delay) { delay_report_ = delay; }
+  uint16_t GetDelayReport() const { return delay_report_; }
+
+  void SetMandatoryCodecPreferred(bool preferred) {
+    mandatory_codec_preferred_ = preferred;
+  }
+  bool IsMandatoryCodecPreferred() const { return mandatory_codec_preferred_; }
 
   /**
    * Check whether any of the flags specified by the bitlags mask is set.
@@ -329,6 +339,8 @@
   uint8_t flags_;
   bool self_initiated_connection_;
   bool is_silenced_;
+  uint16_t delay_report_;
+  bool mandatory_codec_preferred_ = false;
 };
 
 class BtifAvSource {
@@ -346,7 +358,8 @@
 
   bt_status_t Init(
       btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
-      const std::vector<btav_a2dp_codec_config_t>& codec_priorities);
+      const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+      const std::vector<btav_a2dp_codec_config_t>& offloading_preference);
   void Cleanup();
 
   btav_source_callbacks_t* Callbacks() { return callbacks_; }
@@ -422,7 +435,7 @@
     if (peer_address.IsEmpty()) {
       return false;
     }
-    LOG_INFO(LOG_TAG, "%s: peer: %s", __PRETTY_FUNCTION__,
+    LOG_INFO("%s: peer: %s", __PRETTY_FUNCTION__,
              peer_address.ToString().c_str());
     BtifAvPeer* peer = FindPeer(peer_address);
     if (peer == nullptr) {
@@ -494,23 +507,12 @@
       const std::vector<btav_a2dp_codec_config_t>& codec_preferences,
       std::promise<void> peer_ready_promise) {
     // Restart the session if the codec for the active peer is updated
-    bool restart_session =
-        ((active_peer_ == peer_address) && !active_peer_.IsEmpty());
-    if (restart_session) {
+    if (!peer_address.IsEmpty() && active_peer_ == peer_address) {
       btif_a2dp_source_end_session(active_peer_);
     }
 
-    for (auto cp : codec_preferences) {
-      BTIF_TRACE_DEBUG("%s: codec_preference=%s", __func__,
-                       cp.ToString().c_str());
-      btif_a2dp_source_encoder_user_config_update_req(peer_address, cp);
-    }
-    if (restart_session) {
-      btif_a2dp_source_start_session(active_peer_,
-                                     std::move(peer_ready_promise));
-    } else {
-      peer_ready_promise.set_value();
-    }
+    btif_a2dp_source_encoder_user_config_update_req(
+        peer_address, codec_preferences, std::move(peer_ready_promise));
   }
 
   const std::map<RawAddress, BtifAvPeer*>& Peers() const { return peers_; }
@@ -665,6 +667,7 @@
   case BTA_AV_VENDOR_CMD_EVT:      \
   case BTA_AV_META_MSG_EVT:        \
   case BTA_AV_RC_FEAT_EVT:         \
+  case BTA_AV_RC_PSM_EVT:          \
   case BTA_AV_REMOTE_RSP_EVT: {    \
     btif_rc_handler(e, d);         \
   } break;
@@ -685,9 +688,12 @@
                                     btav_audio_state_t state);
 static void btif_av_report_sink_audio_config_state(
     const RawAddress& peer_address, int sample_rate, int channel_count);
+static void btif_av_query_mandatory_codec_priority(
+    const RawAddress& peer_address);
 static void btif_av_source_initiate_av_open_timer_timeout(void* data);
 static void btif_av_sink_initiate_av_open_timer_timeout(void* data);
-static void bta_av_sink_media_callback(tBTA_AV_EVT event,
+static void bta_av_sink_media_callback(const RawAddress& peer_address,
+                                       tBTA_AV_EVT event,
                                        tBTA_AV_MEDIA* p_data);
 
 static BtifAvPeer* btif_av_source_find_peer(const RawAddress& peer_address) {
@@ -737,6 +743,7 @@
     CASE_RETURN_STR(BTA_AV_META_MSG_EVT)
     CASE_RETURN_STR(BTA_AV_REJECT_EVT)
     CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT)
+    CASE_RETURN_STR(BTA_AV_RC_PSM_EVT)
     CASE_RETURN_STR(BTA_AV_OFFLOAD_START_RSP_EVT)
     CASE_RETURN_STR(BTIF_AV_CONNECT_REQ_EVT)
     CASE_RETURN_STR(BTIF_AV_DISCONNECT_REQ_EVT)
@@ -870,7 +877,8 @@
       av_open_on_rc_timer_(nullptr),
       edr_(0),
       flags_(0),
-      self_initiated_connection_(false) {}
+      self_initiated_connection_(false),
+      delay_report_(0) {}
 
 BtifAvPeer::~BtifAvPeer() { alarm_free(av_open_on_rc_timer_); }
 
@@ -946,8 +954,9 @@
 
 bt_status_t BtifAvSource::Init(
     btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
-    const std::vector<btav_a2dp_codec_config_t>& codec_priorities) {
-  LOG_INFO(LOG_TAG, "%s: max_connected_audio_devices=%d", __PRETTY_FUNCTION__,
+    const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+    const std::vector<btav_a2dp_codec_config_t>& offloading_preference) {
+  LOG_INFO("%s: max_connected_audio_devices=%d", __PRETTY_FUNCTION__,
            max_connected_audio_devices);
   if (enabled_) return BT_STATUS_SUCCESS;
   CleanupAllPeers();
@@ -964,6 +973,10 @@
   BTIF_TRACE_DEBUG("a2dp_offload.enable = %d", a2dp_offload_enabled_);
 
   callbacks_ = callbacks;
+  if (a2dp_offload_enabled_) {
+    bluetooth::audio::a2dp::update_codec_offloading_capabilities(
+        offloading_preference);
+  }
   bta_av_co_init(codec_priorities);
 
   if (!btif_a2dp_source_init()) {
@@ -975,7 +988,7 @@
 }
 
 void BtifAvSource::Cleanup() {
-  LOG_INFO(LOG_TAG, "%s", __PRETTY_FUNCTION__);
+  LOG_INFO("%s", __PRETTY_FUNCTION__);
   if (!enabled_) return;
 
   btif_queue_cleanup(UUID_SERVCLASS_AUDIO_SOURCE);
@@ -1041,16 +1054,21 @@
         __PRETTY_FUNCTION__, peer_address.ToString().c_str());
     return nullptr;
   }
+
   // Get the BTA Handle (if known)
   if (bta_handle == kBtaHandleUnknown) {
     auto it = peer_id2bta_handle_.find(peer_id);
-    if (it != peer_id2bta_handle_.end()) {
-      bta_handle = it->second;
+    if (it == peer_id2bta_handle_.end() || it->second == kBtaHandleUnknown) {
+      BTIF_TRACE_ERROR(
+          "%s: Cannot create peer for peer_address=%s : "
+          "cannot convert Peer ID=%d to unique BTA Handle",
+          __PRETTY_FUNCTION__, peer_address.ToString().c_str(), peer_id);
+      return nullptr;
     }
+    bta_handle = it->second;
   }
 
-  LOG_INFO(LOG_TAG,
-           "%s: Create peer: peer_address=%s bta_handle=0x%x peer_id=%d",
+  LOG_INFO("%s: Create peer: peer_address=%s bta_handle=0x%x peer_id=%d",
            __PRETTY_FUNCTION__, peer_address.ToString().c_str(), bta_handle,
            peer_id);
   peer = new BtifAvPeer(peer_address, AVDT_TSEP_SNK, bta_handle, peer_id);
@@ -1096,7 +1114,7 @@
     BtifAvPeer* peer = it->second;
     auto prev_it = it++;
     if (!peer->CanBeDeleted()) continue;
-    LOG_INFO(LOG_TAG, "%s: Deleting idle peer: %s bta_handle=0x%x", __func__,
+    LOG_INFO("%s: Deleting idle peer: %s bta_handle=0x%x", __func__,
              peer->PeerAddress().ToString().c_str(), peer->BtaHandle());
     peer->Cleanup();
     peers_.erase(prev_it);
@@ -1135,7 +1153,18 @@
 
   // Set the BTA Handle for the Peer (if exists)
   BtifAvPeer* peer = FindPeerByPeerId(peer_id);
-  if (peer != nullptr) {
+  if (peer != nullptr && peer->BtaHandle() != bta_handle) {
+    if (peer->BtaHandle() == kBtaHandleUnknown) {
+      BTIF_TRACE_EVENT(
+          "%s: Assign peer: peer_address=%s bta_handle=0x%x peer_id=%d",
+          __PRETTY_FUNCTION__, peer->PeerAddress().ToString().c_str(),
+          bta_handle, peer_id);
+    } else {
+      BTIF_TRACE_WARNING(
+          "%s: Correct peer: peer_address=%s bta_handle=0x%x->0x%x peer_id=%d",
+          __PRETTY_FUNCTION__, peer->PeerAddress().ToString().c_str(),
+          peer->BtaHandle(), bta_handle, peer_id);
+    }
     peer->SetBtaHandle(bta_handle);
   }
 }
@@ -1143,7 +1172,7 @@
 BtifAvSink::~BtifAvSink() { CleanupAllPeers(); }
 
 bt_status_t BtifAvSink::Init(btav_sink_callbacks_t* callbacks) {
-  LOG_INFO(LOG_TAG, "%s", __PRETTY_FUNCTION__);
+  LOG_INFO("%s", __PRETTY_FUNCTION__);
   if (enabled_) return BT_STATUS_SUCCESS;
 
   CleanupAllPeers();
@@ -1162,7 +1191,7 @@
 }
 
 void BtifAvSink::Cleanup() {
-  LOG_INFO(LOG_TAG, "%s", __PRETTY_FUNCTION__);
+  LOG_INFO("%s", __PRETTY_FUNCTION__);
   if (!enabled_) return;
 
   btif_queue_cleanup(UUID_SERVCLASS_AUDIO_SINK);
@@ -1232,13 +1261,17 @@
   // Get the BTA Handle (if known)
   if (bta_handle == kBtaHandleUnknown) {
     auto it = peer_id2bta_handle_.find(peer_id);
-    if (it != peer_id2bta_handle_.end()) {
-      bta_handle = it->second;
+    if (it == peer_id2bta_handle_.end() || it->second == kBtaHandleUnknown) {
+      BTIF_TRACE_ERROR(
+          "%s: Cannot create peer for peer_address=%s : "
+          "cannot convert Peer ID=%d to unique BTA Handle",
+          __PRETTY_FUNCTION__, peer_address.ToString().c_str(), peer_id);
+      return nullptr;
     }
+    bta_handle = it->second;
   }
 
-  LOG_INFO(LOG_TAG,
-           "%s: Create peer: peer_address=%s bta_handle=0x%x peer_id=%d",
+  LOG_INFO("%s: Create peer: peer_address=%s bta_handle=0x%x peer_id=%d",
            __PRETTY_FUNCTION__, peer_address.ToString().c_str(), bta_handle,
            peer_id);
   peer = new BtifAvPeer(peer_address, AVDT_TSEP_SRC, bta_handle, peer_id);
@@ -1287,7 +1320,7 @@
     BtifAvPeer* peer = it->second;
     auto prev_it = it++;
     if (!peer->CanBeDeleted()) continue;
-    LOG_INFO(LOG_TAG, "%s: Deleting idle peer: %s bta_handle=0x%x", __func__,
+    LOG_INFO("%s: Deleting idle peer: %s bta_handle=0x%x", __func__,
              peer->PeerAddress().ToString().c_str(), peer->BtaHandle());
     peer->Cleanup();
     peers_.erase(prev_it);
@@ -1325,7 +1358,18 @@
 
   // Set the BTA Handle for the Peer (if exists)
   BtifAvPeer* peer = FindPeerByPeerId(peer_id);
-  if (peer != nullptr) {
+  if (peer != nullptr && peer->BtaHandle() != bta_handle) {
+    if (peer->BtaHandle() == kBtaHandleUnknown) {
+      BTIF_TRACE_EVENT(
+          "%s: Assign peer: peer_address=%s bta_handle=0x%x peer_id=%d",
+          __PRETTY_FUNCTION__, peer->PeerAddress().ToString().c_str(),
+          bta_handle, peer_id);
+    } else {
+      BTIF_TRACE_WARNING(
+          "%s: Correct peer: peer_address=%s bta_handle=0x%x->0x%x peer_id=%d",
+          __PRETTY_FUNCTION__, peer->PeerAddress().ToString().c_str(),
+          peer->BtaHandle(), bta_handle, peer_id);
+    }
     peer->SetBtaHandle(bta_handle);
   }
 }
@@ -1421,6 +1465,7 @@
         }
         break;
       }
+      btif_av_query_mandatory_codec_priority(peer_.PeerAddress());
       BTA_AvOpen(peer_.PeerAddress(), peer_.BtaHandle(), true,
                  BTA_SEC_AUTHENTICATE, peer_.LocalUuidServiceClass());
       peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpening);
@@ -1493,12 +1538,12 @@
       tBTA_AV_STATUS status = p_bta_data->open.status;
       bool can_connect = true;
 
-      LOG_INFO(
-          LOG_TAG, "%s: Peer %s : event=%s flags=%s status=%d(%s) edr=0x%x",
-          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
-          BtifAvEvent::EventName(event).c_str(), peer_.FlagsToString().c_str(),
-          status, (status == BTA_AV_SUCCESS) ? "SUCCESS" : "FAILED",
-          p_bta_data->open.edr);
+      LOG_INFO("%s: Peer %s : event=%s flags=%s status=%d(%s) edr=0x%x",
+               __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+               BtifAvEvent::EventName(event).c_str(),
+               peer_.FlagsToString().c_str(), status,
+               (status == BTA_AV_SUCCESS) ? "SUCCESS" : "FAILED",
+               p_bta_data->open.edr);
 
       if (p_bta_data->open.status == BTA_AV_SUCCESS) {
         state = BTAV_CONNECTION_STATE_CONNECTED;
@@ -1546,6 +1591,7 @@
     case BTA_AV_VENDOR_CMD_EVT:
     case BTA_AV_META_MSG_EVT:
     case BTA_AV_RC_FEAT_EVT:
+    case BTA_AV_RC_PSM_EVT:
     case BTA_AV_REMOTE_RSP_EVT:
       btif_rc_handler(event, (tBTA_AV*)p_data);
       break;
@@ -1643,12 +1689,12 @@
       int av_state;
       tBTA_AV_STATUS status = p_bta_data->open.status;
 
-      LOG_INFO(
-          LOG_TAG, "%s: Peer %s : event=%s flags=%s status=%d(%s) edr=0x%x",
-          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
-          BtifAvEvent::EventName(event).c_str(), peer_.FlagsToString().c_str(),
-          status, (status == BTA_AV_SUCCESS) ? "SUCCESS" : "FAILED",
-          p_bta_data->open.edr);
+      LOG_INFO("%s: Peer %s : event=%s flags=%s status=%d(%s) edr=0x%x",
+               __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+               BtifAvEvent::EventName(event).c_str(),
+               peer_.FlagsToString().c_str(), status,
+               (status == BTA_AV_SUCCESS) ? "SUCCESS" : "FAILED",
+               p_bta_data->open.edr);
 
       if (p_bta_data->open.status == BTA_AV_SUCCESS) {
         state = BTAV_CONNECTION_STATE_CONNECTED;
@@ -1818,7 +1864,7 @@
       break;  // Ignore
 
     case BTIF_AV_START_STREAM_REQ_EVT:
-      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+      LOG_INFO("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
                peer_.PeerAddress().ToString().c_str(),
                BtifAvEvent::EventName(event).c_str(),
                peer_.FlagsToString().c_str());
@@ -1827,13 +1873,13 @@
       break;
 
     case BTA_AV_START_EVT: {
-      LOG_INFO(LOG_TAG,
-               "%s: Peer %s : event=%s status=%d suspending=%d "
-               "initiator=%d flags=%s",
-               __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
-               BtifAvEvent::EventName(event).c_str(), p_av->start.status,
-               p_av->start.suspending, p_av->start.initiator,
-               peer_.FlagsToString().c_str());
+      LOG_INFO(
+          "%s: Peer %s : event=%s status=%d suspending=%d "
+          "initiator=%d flags=%s",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str(), p_av->start.status,
+          p_av->start.suspending, p_av->start.initiator,
+          peer_.FlagsToString().c_str());
 
       if ((p_av->start.status == BTA_SUCCESS) && p_av->start.suspending)
         return true;
@@ -1841,17 +1887,26 @@
       // If remote tries to start A2DP when DUT is A2DP Source, then Suspend.
       // If A2DP is Sink and call is active, then disconnect the AVDTP channel.
       bool should_suspend = false;
-      if (peer_.IsSink() && !peer_.CheckFlags(BtifAvPeer::kFlagPendingStart |
-                                              BtifAvPeer::kFlagRemoteSuspend)) {
-        LOG(WARNING) << __PRETTY_FUNCTION__ << ": Peer " << peer_.PeerAddress()
-                     << " : trigger Suspend as remote initiated";
-        should_suspend = true;
-      }
+      if (peer_.IsSink()) {
+        if (!peer_.CheckFlags(BtifAvPeer::kFlagPendingStart |
+                              BtifAvPeer::kFlagRemoteSuspend)) {
+          LOG(WARNING) << __PRETTY_FUNCTION__ << ": Peer "
+                       << peer_.PeerAddress()
+                       << " : trigger Suspend as remote initiated";
+          should_suspend = true;
+        } else if (!peer_.IsActivePeer()) {
+          LOG(WARNING) << __PRETTY_FUNCTION__ << ": Peer "
+                       << peer_.PeerAddress()
+                       << " : trigger Suspend as non-active";
+          should_suspend = true;
+        }
 
-      // If peer is A2DP Source, do ACK commands to audio HAL and start media task
-      if (peer_.IsSink() && btif_a2dp_on_started(peer_.PeerAddress(), &p_av->start)) {
-        // Only clear pending flag after acknowledgement
-        peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
+        // If peer is A2DP Source, do ACK commands to audio HAL and start media
+        // task
+        if (btif_a2dp_on_started(peer_.PeerAddress(), &p_av->start)) {
+          // Only clear pending flag after acknowledgement
+          peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
+        }
       }
 
       // Remain in Open state if status failed
@@ -1860,6 +1915,7 @@
       if (peer_.IsSource() && peer_.IsActivePeer()) {
         // Remove flush state, ready for streaming
         btif_a2dp_sink_set_rx_flush(false);
+        btif_a2dp_sink_on_start();
       }
 
       if (should_suspend) {
@@ -1886,17 +1942,20 @@
 
     case BTA_AV_CLOSE_EVT:
       // AVDTP link is closed
-      if (peer_.IsActivePeer()) {
-        btif_a2dp_on_stopped(nullptr);
-      }
-
       // Change state to Idle, send acknowledgement if start is pending
       if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
         BTIF_TRACE_WARNING("%s: Peer %s : failed pending start request",
                            __PRETTY_FUNCTION__,
                            peer_.PeerAddress().ToString().c_str());
-        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
+        tBTA_AV_START av_start = {.chnl = p_av->close.chnl,
+                                  .hndl = p_av->close.hndl,
+                                  .status = BTA_AV_FAIL_STREAM,
+                                  .initiator = true,
+                                  .suspending = true};
+        btif_a2dp_on_started(peer_.PeerAddress(), &av_start);
         // Pending start flag will be cleared when exit current state
+      } else if (peer_.IsActivePeer()) {
+        btif_a2dp_on_stopped(nullptr);
       }
 
       // Inform the application that we are disconnected
@@ -1906,18 +1965,36 @@
       break;
 
     case BTA_AV_RECONFIG_EVT:
-      if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart) &&
-          (p_av->reconfig.status == BTA_AV_SUCCESS)) {
-        LOG_INFO(LOG_TAG,
-                 "%s : Peer %s : Reconfig done - calling BTA_AvStart()",
-                 __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+      if (p_av->reconfig.status != BTA_AV_SUCCESS) {
+        LOG(WARNING) << __PRETTY_FUNCTION__ << ": Peer " << peer_.PeerAddress()
+                     << " : failed reconfiguration";
+        if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
+          LOG(ERROR) << __PRETTY_FUNCTION__ << ": Peer " << peer_.PeerAddress()
+                     << " : cannot proceed to do AvStart";
+          peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
+          btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
+        }
+        if (peer_.IsSink()) {
+          src_disconnect_sink(peer_.PeerAddress());
+        } else if (peer_.IsSource()) {
+          sink_disconnect_src(peer_.PeerAddress());
+        }
+        break;
+      }
+
+      if (peer_.IsActivePeer()) {
+        LOG(INFO) << __PRETTY_FUNCTION__ << " : Peer " << peer_.PeerAddress()
+                  << " : Reconfig done - calling startSession() to audio HAL";
+        std::promise<void> peer_ready_promise;
+        std::future<void> peer_ready_future = peer_ready_promise.get_future();
+        btif_a2dp_source_start_session(peer_.PeerAddress(),
+                                       std::move(peer_ready_promise));
+      }
+      if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
+        LOG(INFO) << __PRETTY_FUNCTION__ << " : Peer " << peer_.PeerAddress()
+                  << " : Reconfig done - calling BTA_AvStart("
+                  << loghex(peer_.BtaHandle()) << ")";
         BTA_AvStart(peer_.BtaHandle());
-      } else if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
-        BTIF_TRACE_WARNING("%s: Peer %s : failed reconfiguration",
-                           __PRETTY_FUNCTION__,
-                           peer_.PeerAddress().ToString().c_str());
-        peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
-        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
       }
       break;
 
@@ -1965,6 +2042,8 @@
   // We are again in started state, clear any remote suspend flags
   peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend);
 
+  btif_a2dp_sink_set_rx_flush(false);
+
   // Report that we have entered the Streaming stage. Usually, this should
   // be followed by focus grant. See update_audio_focus_state()
   btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STARTED);
@@ -1990,7 +2069,7 @@
       break;  // Ignore
 
     case BTIF_AV_START_STREAM_REQ_EVT:
-      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+      LOG_INFO("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
                peer_.PeerAddress().ToString().c_str(),
                BtifAvEvent::EventName(event).c_str(),
                peer_.FlagsToString().c_str());
@@ -2001,7 +2080,7 @@
     // FIXME -- use suspend = true always to work around issue with BTA AV
     case BTIF_AV_STOP_STREAM_REQ_EVT:
     case BTIF_AV_SUSPEND_STREAM_REQ_EVT:
-      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+      LOG_INFO("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
                peer_.PeerAddress().ToString().c_str(),
                BtifAvEvent::EventName(event).c_str(),
                peer_.FlagsToString().c_str());
@@ -2013,15 +2092,14 @@
       // always overrides.
       peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend);
 
-      if (peer_.IsSink()) {
+      if (peer_.IsSink() &&
+          (peer_.IsActivePeer() || !btif_av_stream_started_ready())) {
         // Immediately stop transmission of frames while suspend is pending
-        if (peer_.IsActivePeer()) {
-          if (event == BTIF_AV_STOP_STREAM_REQ_EVT) {
-            btif_a2dp_on_stopped(nullptr);
-          } else {
-            // (event == BTIF_AV_SUSPEND_STREAM_REQ_EVT)
-            btif_a2dp_source_set_tx_flush(true);
-          }
+        if (event == BTIF_AV_STOP_STREAM_REQ_EVT) {
+          btif_a2dp_on_stopped(nullptr);
+        } else {
+          // ensure tx frames are immediately suspended
+          btif_a2dp_source_set_tx_flush(true);
         }
       } else if (peer_.IsSource()) {
         btif_a2dp_on_stopped(nullptr);
@@ -2030,7 +2108,7 @@
       break;
 
     case BTIF_AV_DISCONNECT_REQ_EVT:
-      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+      LOG_INFO("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
                peer_.PeerAddress().ToString().c_str(),
                BtifAvEvent::EventName(event).c_str(),
                peer_.FlagsToString().c_str());
@@ -2050,14 +2128,15 @@
       break;
 
     case BTA_AV_SUSPEND_EVT: {
-      LOG_INFO(LOG_TAG,
-               "%s: Peer %s : event=%s status=%d initiator=%d flags=%s",
+      LOG_INFO("%s: Peer %s : event=%s status=%d initiator=%d flags=%s",
                __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
                BtifAvEvent::EventName(event).c_str(), p_av->suspend.status,
                p_av->suspend.initiator, peer_.FlagsToString().c_str());
 
-      // A2DP suspended, stop A2DP encoder/decoder until resumed
-      btif_a2dp_on_suspended(&p_av->suspend);
+      // A2DP suspended, stop A2DP encoder / decoder until resumed
+      if (peer_.IsActivePeer() || !btif_av_stream_started_ready()) {
+        btif_a2dp_on_suspended(&p_av->suspend);
+      }
 
       // If not successful, remain in current state
       if (p_av->suspend.status != BTA_AV_SUCCESS) {
@@ -2083,15 +2162,13 @@
         state = BTAV_AUDIO_STATE_STOPPED;
       }
 
-      // Suspend completed, clear pending status
-      peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending);
-
       btif_report_audio_state(peer_.PeerAddress(), state);
+      // Suspend completed, clear local pending flags while entering Opened
       peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpened);
     } break;
 
     case BTA_AV_STOP_EVT:
-      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+      LOG_INFO("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
                peer_.PeerAddress().ToString().c_str(),
                BtifAvEvent::EventName(event).c_str(),
                peer_.FlagsToString().c_str());
@@ -2099,7 +2176,11 @@
       peer_.SetFlags(BtifAvPeer::kFlagPendingStop);
       peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending);
 
-      btif_a2dp_on_stopped(&p_av->suspend);
+      // Don't change the encoder and audio provider state by a non-active peer
+      // since they are shared between peers
+      if (peer_.IsActivePeer() || !btif_av_stream_started_ready()) {
+        btif_a2dp_on_stopped(&p_av->suspend);
+      }
 
       btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STOPPED);
 
@@ -2110,7 +2191,7 @@
       break;
 
     case BTA_AV_CLOSE_EVT:
-      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+      LOG_INFO("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
                peer_.PeerAddress().ToString().c_str(),
                BtifAvEvent::EventName(event).c_str(),
                peer_.FlagsToString().c_str());
@@ -2289,7 +2370,7 @@
  */
 static void btif_report_connection_state(const RawAddress& peer_address,
                                          btav_connection_state_t state) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%d", __func__,
+  LOG_INFO("%s: peer_address=%s state=%d", __func__,
            peer_address.ToString().c_str(), state);
 
   if (btif_av_source.Enabled()) {
@@ -2314,7 +2395,7 @@
  */
 static void btif_report_audio_state(const RawAddress& peer_address,
                                     btav_audio_state_t state) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%d", __func__,
+  LOG_INFO("%s: peer_address=%s state=%d", __func__,
            peer_address.ToString().c_str(), state);
 
   if (btif_av_source.Enabled()) {
@@ -2354,7 +2435,7 @@
  */
 static void btif_av_report_sink_audio_config_state(
     const RawAddress& peer_address, int sample_rate, int channel_count) {
-  LOG_INFO(LOG_TAG, "%s: Peer %s : sample_rate=%d channel_count=%d", __func__,
+  LOG_INFO("%s: Peer %s : sample_rate=%d channel_count=%d", __func__,
            peer_address.ToString().c_str(), sample_rate, channel_count);
   if (btif_av_sink.Enabled()) {
     do_in_jni_thread(FROM_HERE,
@@ -2364,6 +2445,36 @@
 }
 
 /**
+ * Call out to JNI / JAVA layers to retrieve whether the mandatory codec is more
+ * preferred than others.
+ *
+ * @param peer_address the peer address
+ */
+static void btif_av_query_mandatory_codec_priority(
+    const RawAddress& peer_address) {
+  auto query_priority = [](const RawAddress& peer_address) {
+    auto apply_priority = [](const RawAddress& peer_address, bool preferred) {
+      BtifAvPeer* peer = btif_av_source_find_peer(peer_address);
+      if (peer == nullptr) {
+        BTIF_TRACE_WARNING(
+            "btif_av_query_mandatory_codec_priority: peer is null");
+        return;
+      }
+      peer->SetMandatoryCodecPreferred(preferred);
+    };
+    bool preferred =
+        btif_av_source.Callbacks()->mandatory_codec_preferred_cb(peer_address);
+    if (preferred) {
+      do_in_main_thread(
+          FROM_HERE, base::BindOnce(apply_priority, peer_address, preferred));
+    }
+  };
+  if (btif_av_source.Enabled()) {
+    do_in_jni_thread(FROM_HERE, base::BindOnce(query_priority, peer_address));
+  }
+}
+
+/**
  * Process BTIF or BTA AV or BTA AVRCP events. The processing is done on the
  * JNI thread.
  *
@@ -2539,6 +2650,11 @@
       peer_address = rc_feat.peer_addr;
       break;
     }
+    case BTA_AV_RC_PSM_EVT: {
+      const tBTA_AV_RC_PSM& rc_psm = p_data->rc_cover_art_psm;
+      peer_address = rc_psm.peer_addr;
+      break;
+    }
   }
   BTIF_TRACE_DEBUG("%s: peer_address=%s bta_handle=0x%x", __func__,
                    peer_address.ToString().c_str(), bta_handle);
@@ -2563,14 +2679,17 @@
 }
 
 // TODO: All processing should be done on the JNI thread
-static void bta_av_sink_media_callback(tBTA_AV_EVT event,
+static void bta_av_sink_media_callback(const RawAddress& peer_address,
+                                       tBTA_AV_EVT event,
                                        tBTA_AV_MEDIA* p_data) {
   BTIF_TRACE_EVENT("%s: event=%d", __func__, event);
+  BTIF_TRACE_EVENT("%s: address=%s", __func__,
+                   (p_data->avk_config.bd_addr.ToString().c_str()));
 
   switch (event) {
     case BTA_AV_SINK_MEDIA_DATA_EVT: {
-      BtifAvPeer* peer = btif_av_sink_find_peer(btif_av_sink.ActivePeer());
-      if (peer != nullptr) {
+      BtifAvPeer* peer = btif_av_sink_find_peer(peer_address);
+      if (peer != nullptr && peer->IsActivePeer()) {
         int state = peer->StateMachine().StateId();
         if ((state == BtifAvStateMachine::kStateStarted) ||
             (state == BtifAvStateMachine::kStateOpened)) {
@@ -2616,10 +2735,11 @@
 // Initializes the AV interface for source mode
 static bt_status_t init_src(
     btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
-    std::vector<btav_a2dp_codec_config_t> codec_priorities) {
+    const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+    const std::vector<btav_a2dp_codec_config_t>& offloading_preference) {
   BTIF_TRACE_EVENT("%s", __func__);
   return btif_av_source.Init(callbacks, max_connected_audio_devices,
-                             codec_priorities);
+                             codec_priorities, offloading_preference);
 }
 
 // Initializes the AV interface for sink mode
@@ -2645,20 +2765,25 @@
   BTIF_TRACE_EVENT("%s: peer_address=%s uuid=0x%x", __func__,
                    peer_address->ToString().c_str(), uuid);
 
-  BtifAvPeer* peer = nullptr;
-  if (uuid == UUID_SERVCLASS_AUDIO_SOURCE) {
-    peer = btif_av_source.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
-    if (peer == nullptr) {
-      return BT_STATUS_FAIL;
+  auto connection_task = [](RawAddress* peer_address, uint16_t uuid) {
+    BtifAvPeer* peer = nullptr;
+    if (uuid == UUID_SERVCLASS_AUDIO_SOURCE) {
+      peer = btif_av_source.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
+    } else if (uuid == UUID_SERVCLASS_AUDIO_SINK) {
+      peer = btif_av_sink.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
     }
-  } else if (uuid == UUID_SERVCLASS_AUDIO_SINK) {
-    peer = btif_av_sink.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
     if (peer == nullptr) {
-      return BT_STATUS_FAIL;
+      btif_queue_advance();
+      return;
     }
+    peer->StateMachine().ProcessEvent(BTIF_AV_CONNECT_REQ_EVT, nullptr);
+  };
+  bt_status_t status = do_in_main_thread(
+      FROM_HERE, base::BindOnce(connection_task, peer_address, uuid));
+  if (status != BT_STATUS_SUCCESS) {
+    LOG(ERROR) << __func__ << ": can't post connection task to main_thread";
   }
-  peer->StateMachine().ProcessEvent(BTIF_AV_CONNECT_REQ_EVT, nullptr);
-  return BT_STATUS_SUCCESS;
+  return status;
 }
 
 static void set_source_silence_peer_int(const RawAddress& peer_address,
@@ -2717,7 +2842,7 @@
 }
 
 static bt_status_t sink_connect_src(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: Peer %s", __func__, peer_address.ToString().c_str());
+  LOG_INFO("%s: Peer %s", __func__, peer_address.ToString().c_str());
 
   if (!btif_av_sink.Enabled()) {
     BTIF_TRACE_WARNING("%s: BTIF AV Sink is not enabled", __func__);
@@ -2730,7 +2855,7 @@
 }
 
 static bt_status_t src_disconnect_sink(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: Peer %s", __func__, peer_address.ToString().c_str());
+  LOG_INFO("%s: Peer %s", __func__, peer_address.ToString().c_str());
 
   if (!btif_av_source.Enabled()) {
     BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__);
@@ -2746,7 +2871,7 @@
 }
 
 static bt_status_t sink_disconnect_src(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: Peer %s", __func__, peer_address.ToString().c_str());
+  LOG_INFO("%s: Peer %s", __func__, peer_address.ToString().c_str());
 
   if (!btif_av_sink.Enabled()) {
     BTIF_TRACE_WARNING("%s: BTIF AV Sink is not enabled", __func__);
@@ -2761,6 +2886,28 @@
                             peer_address, kBtaHandleUnknown, btif_av_event));
 }
 
+static bt_status_t sink_set_active_device(const RawAddress& peer_address) {
+  BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str());
+
+  if (!btif_av_sink.Enabled()) {
+    LOG(WARNING) << __func__ << ": BTIF AV Source is not enabled";
+    return BT_STATUS_NOT_READY;
+  }
+
+  std::promise<void> peer_ready_promise;
+  std::future<void> peer_ready_future = peer_ready_promise.get_future();
+  bt_status_t status = do_in_main_thread(
+      FROM_HERE, base::BindOnce(&set_active_peer_int,
+                                AVDT_TSEP_SRC,  // peer_sep
+                                peer_address, std::move(peer_ready_promise)));
+  if (status == BT_STATUS_SUCCESS) {
+    peer_ready_future.wait();
+  } else {
+    LOG(WARNING) << __func__ << ": BTIF AV Sink fails to change peer";
+  }
+  return status;
+}
+
 static bt_status_t src_set_silence_sink(const RawAddress& peer_address,
                                         bool silence) {
   BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str());
@@ -2805,6 +2952,11 @@
     return BT_STATUS_NOT_READY;
   }
 
+  if (peer_address.IsEmpty()) {
+    LOG(WARNING) << __func__ << ": BTIF AV Source needs peer to config";
+    return BT_STATUS_PARM_INVALID;
+  }
+
   std::promise<void> peer_ready_promise;
   std::future<void> peer_ready_future = peer_ready_promise.get_future();
   bt_status_t status = do_in_main_thread(
@@ -2844,10 +2996,14 @@
 };
 
 static const btav_sink_interface_t bt_av_sink_interface = {
-    sizeof(btav_sink_interface_t), init_sink,    sink_connect_src,
-    sink_disconnect_src,           cleanup_sink, update_audio_focus_state,
+    sizeof(btav_sink_interface_t),
+    init_sink,
+    sink_connect_src,
+    sink_disconnect_src,
+    cleanup_sink,
+    update_audio_focus_state,
     update_audio_track_gain,
-};
+    sink_set_active_device};
 
 RawAddress btif_av_source_active_peer(void) {
   return btif_av_source.ActivePeer();
@@ -2857,7 +3013,7 @@
 bool btif_av_is_sink_enabled(void) { return btif_av_sink.Enabled(); }
 
 void btif_av_stream_start(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   btif_av_source_dispatch_sm_event(btif_av_source_active_peer(),
                                    BTIF_AV_START_STREAM_REQ_EVT);
 }
@@ -2884,7 +3040,7 @@
 }
 
 void btif_av_stream_stop(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s peer %s", __func__, peer_address.ToString().c_str());
+  LOG_INFO("%s peer %s", __func__, peer_address.ToString().c_str());
 
   if (!peer_address.IsEmpty()) {
     btif_av_source_dispatch_sm_event(peer_address, BTIF_AV_STOP_STREAM_REQ_EVT);
@@ -2897,20 +3053,20 @@
 }
 
 void btif_av_stream_suspend(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   // The active peer might have changed and we might be in the process
   // of reconfiguring the stream. We need to suspend the appropriate peer(s).
   src_do_suspend_in_main_thread(BTIF_AV_SUSPEND_STREAM_REQ_EVT);
 }
 
 void btif_av_stream_start_offload(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   btif_av_source_dispatch_sm_event(btif_av_source_active_peer(),
                                    BTIF_AV_OFFLOAD_START_REQ_EVT);
 }
 
 void btif_av_src_disconnect_sink(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer %s", __func__, peer_address.ToString().c_str());
+  LOG_INFO("%s: peer %s", __func__, peer_address.ToString().c_str());
   src_disconnect_sink(peer_address);
 }
 
@@ -2928,7 +3084,7 @@
   }
 
   int state = peer->StateMachine().StateId();
-  LOG_INFO(LOG_TAG, "%s: Peer %s : state=%d, flags=%s", __func__,
+  LOG_INFO("%s: Peer %s : state=%d, flags=%s", __func__,
            peer->PeerAddress().ToString().c_str(), state,
            peer->FlagsToString().c_str());
   // check if we are remotely suspended or stop is pending
@@ -2957,7 +3113,7 @@
   } else {
     ready = (state == BtifAvStateMachine::kStateStarted);
   }
-  LOG_INFO(LOG_TAG, "%s: Peer %s : state=%d flags=%s ready=%d", __func__,
+  LOG_INFO("%s: Peer %s : state=%d flags=%s ready=%d", __func__,
            peer->PeerAddress().ToString().c_str(), state,
            peer->FlagsToString().c_str(), ready);
 
@@ -3036,7 +3192,7 @@
     tBTA_AV_FEAT features = BTA_AV_FEAT_NO_SCO_SSPD | BTA_AV_FEAT_RCCT |
                             BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR |
                             BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_RCTG |
-                            BTA_AV_FEAT_BROWSE;
+                            BTA_AV_FEAT_BROWSE | BTA_AV_FEAT_COVER_ARTWORK;
     BTA_AvEnable(BTA_SEC_AUTHENTICATE, features, bta_av_sink_callback);
     btif_av_sink.RegisterAllBtaHandles();
     return BT_STATUS_SUCCESS;
@@ -3135,9 +3291,19 @@
   return (is_connected && is3mbps);
 }
 
+bool btif_av_peer_prefers_mandatory_codec(const RawAddress& peer_address) {
+  BtifAvPeer* peer = btif_av_find_peer(peer_address);
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: No peer found for peer_address=%s", __func__,
+                       peer_address.ToString().c_str());
+    return false;
+  }
+  return peer->IsMandatoryCodecPreferred();
+}
+
 void btif_av_acl_disconnected(const RawAddress& peer_address) {
   // Inform the application that ACL is disconnected and move to idle state
-  LOG_INFO(LOG_TAG, "%s: Peer %s : ACL Disconnected", __func__,
+  LOG_INFO("%s: Peer %s : ACL Disconnected", __func__,
            peer_address.ToString().c_str());
 
   if (btif_av_source.Enabled()) {
@@ -3187,6 +3353,9 @@
   dprintf(fd, "    Support 3Mbps: %s\n", peer.Is3Mbps() ? "true" : "false");
   dprintf(fd, "    Self Initiated Connection: %s\n",
           peer.SelfInitiatedConnection() ? "true" : "false");
+  dprintf(fd, "    Delay Reporting: %u\n", peer.GetDelayReport());
+  dprintf(fd, "    Codec Preferred: %s\n",
+          peer.IsMandatoryCodecPreferred() ? "Mandatory" : "Optional");
 }
 
 static void btif_debug_av_source_dump(int fd) {
@@ -3221,9 +3390,23 @@
   btif_debug_av_sink_dump(fd);
 }
 
-void btif_av_set_audio_delay(uint16_t delay) {
+void btif_av_set_audio_delay(const RawAddress& peer_address, uint16_t delay) {
   btif_a2dp_control_set_audio_delay(delay);
-  bluetooth::audio::a2dp::set_remote_delay(delay);
+  BtifAvPeer* peer = btif_av_find_peer(peer_address);
+  if (peer != nullptr && peer->IsSink()) {
+    peer->SetDelayReport(delay);
+    if (peer->IsActivePeer()) {
+      bluetooth::audio::a2dp::set_remote_delay(peer->GetDelayReport());
+    }
+  }
+}
+
+uint16_t btif_av_get_audio_delay() {
+  BtifAvPeer* peer = btif_av_find_active_peer();
+  if (peer != nullptr && peer->IsSink()) {
+    return peer->GetDelayReport();
+  }
+  return 0;
 }
 
 void btif_av_reset_audio_delay(void) { btif_a2dp_control_reset_audio_delay(); }
@@ -3232,6 +3415,18 @@
   return btif_av_source.A2dpOffloadEnabled();
 }
 
+bool btif_av_is_a2dp_offload_running() {
+  if (!btif_av_is_a2dp_offload_enabled()) {
+    return false;
+  }
+  if (!bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
+    // since android::hardware::bluetooth::a2dp::V1_0 deprecated, offloading
+    // is supported by Bluetooth Audio HAL 2.0 only.
+    return false;
+  }
+  return bluetooth::audio::a2dp::is_hal_2_0_offloading();
+}
+
 bool btif_av_is_peer_silenced(const RawAddress& peer_address) {
   return btif_av_source.IsPeerSilenced(peer_address);
 }
diff --git a/btif/src/btif_avrcp_audio_track.cc b/btif/src/btif_avrcp_audio_track.cc
index 5cf94e2..ca9ce9a 100644
--- a/btif/src/btif_avrcp_audio_track.cc
+++ b/btif/src/btif_avrcp_audio_track.cc
@@ -43,7 +43,7 @@
 
 void* BtifAvrcpAudioTrackCreate(int trackFreq, int bitsPerSample,
                                 int channelCount) {
-  LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btCreateTrack freq %d bps %d channel %d ",
+  LOG_VERBOSE("%s Track.cpp: btCreateTrack freq %d bps %d channel %d ",
               __func__, trackFreq, bitsPerSample, channelCount);
 
   AAudioStreamBuilder* builder;
@@ -76,36 +76,36 @@
 
 void BtifAvrcpAudioTrackStart(void* handle) {
   if (handle == NULL) {
-    LOG_ERROR(LOG_TAG, "%s: handle is null!", __func__);
+    LOG_ERROR("%s: handle is null!", __func__);
     return;
   }
   BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
   CHECK(trackHolder != NULL);
   CHECK(trackHolder->stream != NULL);
-  LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btStartTrack", __func__);
+  LOG_VERBOSE("%s Track.cpp: btStartTrack", __func__);
   AAudioStream_requestStart(trackHolder->stream);
 }
 
 void BtifAvrcpAudioTrackStop(void* handle) {
   if (handle == NULL) {
-    LOG_DEBUG(LOG_TAG, "%s handle is null.", __func__);
+    LOG_DEBUG("%s handle is null.", __func__);
     return;
   }
   BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
   if (trackHolder != NULL && trackHolder->stream != NULL) {
-    LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btStartTrack", __func__);
+    LOG_VERBOSE("%s Track.cpp: btStartTrack", __func__);
     AAudioStream_requestStop(trackHolder->stream);
   }
 }
 
 void BtifAvrcpAudioTrackDelete(void* handle) {
   if (handle == NULL) {
-    LOG_DEBUG(LOG_TAG, "%s handle is null.", __func__);
+    LOG_DEBUG("%s handle is null.", __func__);
     return;
   }
   BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
   if (trackHolder != NULL && trackHolder->stream != NULL) {
-    LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btStartTrack", __func__);
+    LOG_VERBOSE("%s Track.cpp: btStartTrack", __func__);
     AAudioStream_close(trackHolder->stream);
     delete trackHolder->buffer;
     delete trackHolder;
@@ -121,12 +121,12 @@
 
 void BtifAvrcpAudioTrackPause(void* handle) {
   if (handle == NULL) {
-    LOG_DEBUG(LOG_TAG, "%s handle is null.", __func__);
+    LOG_DEBUG("%s handle is null.", __func__);
     return;
   }
   BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
   if (trackHolder != NULL && trackHolder->stream != NULL) {
-    LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btPauseTrack", __func__);
+    LOG_VERBOSE("%s Track.cpp: btPauseTrack", __func__);
     AAudioStream_requestPause(trackHolder->stream);
     AAudioStream_requestFlush(trackHolder->stream);
   }
@@ -134,7 +134,7 @@
 
 void BtifAvrcpSetAudioTrackGain(void* handle, float gain) {
   if (handle == NULL) {
-    LOG_DEBUG(LOG_TAG, "%s handle is null.", __func__);
+    LOG_DEBUG("%s handle is null.", __func__);
     return;
   }
   // Does nothing right now
@@ -218,8 +218,8 @@
         trackHolder->stream, trackHolder->buffer,
         transcodedCount / (sampleSize * trackHolder->channelCount),
         kTimeoutNanos);
-    LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btWriteData len = %d ret = %d",
-                __func__, bufferLength, retval);
+    LOG_VERBOSE("%s Track.cpp: btWriteData len = %d ret = %d", __func__,
+                bufferLength, retval);
   } while (transcodedCount < bufferLength);
 
   return transcodedCount;
diff --git a/btif/src/btif_ble_scanner.cc b/btif/src/btif_ble_scanner.cc
index b314993..5aaa60f 100644
--- a/btif/src/btif_ble_scanner.cc
+++ b/btif/src/btif_ble_scanner.cc
@@ -128,8 +128,7 @@
         if (remote_name_len > BD_NAME_LEN + 1 ||
             (remote_name_len == BD_NAME_LEN + 1 &&
              p_eir_remote_name[BD_NAME_LEN] != '\0')) {
-          LOG_INFO(LOG_TAG,
-                   "%s dropping invalid packet - device name too long: %d",
+          LOG_INFO("%s dropping invalid packet - device name too long: %d",
                    __func__, remote_name_len);
           return;
         }
@@ -139,8 +138,8 @@
         if (remote_name_len < BD_NAME_LEN + 1)
           bdname.name[remote_name_len] = '\0';
 
-        LOG_VERBOSE(LOG_TAG, "%s BLE device name=%s len=%d dev_type=%d",
-                    __func__, bdname.name, remote_name_len, device_type);
+        LOG_VERBOSE("%s BLE device name=%s len=%d dev_type=%d", __func__,
+                    bdname.name, remote_name_len, device_type);
         btif_dm_update_ble_remote_properties(bd_addr, bdname.name, device_type);
       }
     }
diff --git a/btif/src/btif_bqr.cc b/btif/src/btif_bqr.cc
index 3f53e74..8b038ad 100644
--- a/btif/src/btif_bqr.cc
+++ b/btif/src/btif_bqr.cc
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
+#include <base/logging.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <statslog.h>
+#include <stdio.h>
+#include <sys/stat.h>
 
 #include "btif_bqr.h"
 #include "btif_dm.h"
@@ -32,60 +37,110 @@
 static std::unique_ptr<LeakyBondedQueue<BqrVseSubEvt>> kpBqrEventQueue(
     new LeakyBondedQueue<BqrVseSubEvt>(kBqrEventQueueSize));
 
-bool BqrVseSubEvt::ParseBqrEvt(uint8_t length, uint8_t* p_param_buf) {
-  if (length < kBqrParamTotalLen) {
+void BqrVseSubEvt::ParseBqrLinkQualityEvt(uint8_t length,
+                                          uint8_t* p_param_buf) {
+  if (length < kLinkQualityParamTotalLen) {
     LOG(FATAL) << __func__
                << ": Parameter total length: " << std::to_string(length)
                << " is abnormal. It shall be not shorter than: "
-               << std::to_string(kBqrParamTotalLen);
-    return false;
+               << std::to_string(kLinkQualityParamTotalLen);
+    return;
   }
 
-  STREAM_TO_UINT8(quality_report_id_, p_param_buf);
-  STREAM_TO_UINT8(packet_types_, p_param_buf);
-  STREAM_TO_UINT16(connection_handle_, p_param_buf);
-  STREAM_TO_UINT8(connection_role_, p_param_buf);
-  STREAM_TO_UINT8(tx_power_level_, p_param_buf);
-  STREAM_TO_INT8(rssi_, p_param_buf);
-  STREAM_TO_UINT8(snr_, p_param_buf);
-  STREAM_TO_UINT8(unused_afh_channel_count_, p_param_buf);
-  STREAM_TO_UINT8(afh_select_unideal_channel_count_, p_param_buf);
-  STREAM_TO_UINT16(lsto_, p_param_buf);
-  STREAM_TO_UINT32(connection_piconet_clock_, p_param_buf);
-  STREAM_TO_UINT32(retransmission_count_, p_param_buf);
-  STREAM_TO_UINT32(no_rx_count_, p_param_buf);
-  STREAM_TO_UINT32(nak_count_, p_param_buf);
-  STREAM_TO_UINT32(last_tx_ack_timestamp_, p_param_buf);
-  STREAM_TO_UINT32(flow_off_count_, p_param_buf);
-  STREAM_TO_UINT32(last_flow_on_timestamp_, p_param_buf);
-  STREAM_TO_UINT32(buffer_overflow_bytes_, p_param_buf);
-  STREAM_TO_UINT32(buffer_underflow_bytes_, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.quality_report_id, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.packet_types, p_param_buf);
+  STREAM_TO_UINT16(bqr_link_quality_event_.connection_handle, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.connection_role, p_param_buf);
+  STREAM_TO_INT8(bqr_link_quality_event_.tx_power_level, p_param_buf);
+  STREAM_TO_INT8(bqr_link_quality_event_.rssi, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.snr, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.unused_afh_channel_count,
+                  p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.afh_select_unideal_channel_count,
+                  p_param_buf);
+  STREAM_TO_UINT16(bqr_link_quality_event_.lsto, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.connection_piconet_clock,
+                   p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.retransmission_count, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.no_rx_count, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.nak_count, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.last_tx_ack_timestamp, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.flow_off_count, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.last_flow_on_timestamp, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.buffer_overflow_bytes, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.buffer_underflow_bytes, p_param_buf);
 
   const auto now = system_clock::to_time_t(system_clock::now());
   localtime_r(&now, &tm_timestamp_);
+}
 
-  return true;
+void BqrVseSubEvt::WriteLmpLlTraceLogFile(int fd, uint8_t length,
+                                          uint8_t* p_param_buf) {
+  const auto now = system_clock::to_time_t(system_clock::now());
+  localtime_r(&now, &tm_timestamp_);
+
+  STREAM_TO_UINT8(bqr_log_dump_event_.quality_report_id, p_param_buf);
+  STREAM_TO_UINT16(bqr_log_dump_event_.connection_handle, p_param_buf);
+  length -= kLogDumpParamTotalLen;
+  bqr_log_dump_event_.vendor_specific_parameter = p_param_buf;
+
+  std::stringstream ss_log;
+  ss_log << "\n"
+         << std::put_time(&tm_timestamp_, "%m-%d %H:%M:%S ")
+         << "Handle: " << loghex(bqr_log_dump_event_.connection_handle)
+         << " VSP: ";
+
+  TEMP_FAILURE_RETRY(write(fd, ss_log.str().c_str(), ss_log.str().size()));
+  TEMP_FAILURE_RETRY(
+      write(fd, bqr_log_dump_event_.vendor_specific_parameter, length));
+  LmpLlMessageTraceCounter++;
+}
+
+void BqrVseSubEvt::WriteBtSchedulingTraceLogFile(int fd, uint8_t length,
+                                                 uint8_t* p_param_buf) {
+  const auto now = system_clock::to_time_t(system_clock::now());
+  localtime_r(&now, &tm_timestamp_);
+
+  STREAM_TO_UINT8(bqr_log_dump_event_.quality_report_id, p_param_buf);
+  STREAM_TO_UINT16(bqr_log_dump_event_.connection_handle, p_param_buf);
+  length -= kLogDumpParamTotalLen;
+  bqr_log_dump_event_.vendor_specific_parameter = p_param_buf;
+
+  std::stringstream ss_log;
+  ss_log << "\n"
+         << std::put_time(&tm_timestamp_, "%m-%d %H:%M:%S ")
+         << "Handle: " << loghex(bqr_log_dump_event_.connection_handle)
+         << " VSP: ";
+
+  TEMP_FAILURE_RETRY(write(fd, ss_log.str().c_str(), ss_log.str().size()));
+  TEMP_FAILURE_RETRY(
+      write(fd, bqr_log_dump_event_.vendor_specific_parameter, length));
+  BtSchedulingTraceCounter++;
 }
 
 std::string BqrVseSubEvt::ToString() const {
-  std::stringstream ss_return_string;
-  ss_return_string << QualityReportIdToString(quality_report_id_)
-                   << ", Handle: " << loghex(connection_handle_) << ", "
-                   << PacketTypeToString(packet_types_) << ", "
-                   << ((connection_role_ == 0) ? "Master" : "Slave ")
-                   << ", PwLv: " << loghex(tx_power_level_)
-                   << ", RSSI: " << std::to_string(rssi_)
-                   << ", SNR: " << std::to_string(snr_) << ", UnusedCh: "
-                   << std::to_string(unused_afh_channel_count_)
-                   << ", UnidealCh: "
-                   << std::to_string(afh_select_unideal_channel_count_)
-                   << ", ReTx: " << std::to_string(retransmission_count_)
-                   << ", NoRX: " << std::to_string(no_rx_count_)
-                   << ", NAK: " << std::to_string(nak_count_)
-                   << ", FlowOff: " << std::to_string(flow_off_count_)
-                   << ", OverFlow: " << std::to_string(buffer_overflow_bytes_)
-                   << ", UndFlow: " << std::to_string(buffer_underflow_bytes_);
-  return ss_return_string.str();
+  std::stringstream ss;
+  ss << QualityReportIdToString(bqr_link_quality_event_.quality_report_id)
+     << ", Handle: " << loghex(bqr_link_quality_event_.connection_handle)
+     << ", " << PacketTypeToString(bqr_link_quality_event_.packet_types) << ", "
+     << ((bqr_link_quality_event_.connection_role == 0) ? "Master" : "Slave ")
+     << ", PwLv: " << std::to_string(bqr_link_quality_event_.tx_power_level)
+     << ", RSSI: " << std::to_string(bqr_link_quality_event_.rssi)
+     << ", SNR: " << std::to_string(bqr_link_quality_event_.snr)
+     << ", UnusedCh: "
+     << std::to_string(bqr_link_quality_event_.unused_afh_channel_count)
+     << ", UnidealCh: "
+     << std::to_string(bqr_link_quality_event_.afh_select_unideal_channel_count)
+     << ", ReTx: "
+     << std::to_string(bqr_link_quality_event_.retransmission_count)
+     << ", NoRX: " << std::to_string(bqr_link_quality_event_.no_rx_count)
+     << ", NAK: " << std::to_string(bqr_link_quality_event_.nak_count)
+     << ", FlowOff: " << std::to_string(bqr_link_quality_event_.flow_off_count)
+     << ", OverFlow: "
+     << std::to_string(bqr_link_quality_event_.buffer_overflow_bytes)
+     << ", UndFlow: "
+     << std::to_string(bqr_link_quality_event_.buffer_underflow_bytes);
+  return ss.str();
 }
 
 std::string QualityReportIdToString(uint8_t quality_report_id) {
@@ -166,46 +221,6 @@
   }
 }
 
-void AddBqrEventToQueue(uint8_t length, uint8_t* p_stream) {
-  std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
-  if (!p_bqr_event->ParseBqrEvt(length, p_stream)) {
-    LOG(WARNING) << __func__ << ": Fail to parse BQR sub event.";
-    return;
-  }
-
-  LOG(WARNING) << *p_bqr_event;
-  int ret = android::util::stats_write(
-      android::util::BLUETOOTH_QUALITY_REPORT_REPORTED,
-      p_bqr_event->quality_report_id_, p_bqr_event->packet_types_,
-      p_bqr_event->connection_handle_, p_bqr_event->connection_role_,
-      p_bqr_event->tx_power_level_, p_bqr_event->rssi_, p_bqr_event->snr_,
-      p_bqr_event->unused_afh_channel_count_,
-      p_bqr_event->afh_select_unideal_channel_count_, p_bqr_event->lsto_,
-      p_bqr_event->connection_piconet_clock_,
-      p_bqr_event->retransmission_count_, p_bqr_event->no_rx_count_,
-      p_bqr_event->nak_count_, p_bqr_event->last_tx_ack_timestamp_,
-      p_bqr_event->flow_off_count_, p_bqr_event->last_flow_on_timestamp_,
-      p_bqr_event->buffer_overflow_bytes_,
-      p_bqr_event->buffer_underflow_bytes_);
-  if (ret < 0) {
-    LOG(WARNING) << __func__ << ": failed to log BQR event to statsd, error "
-                 << ret;
-  }
-  kpBqrEventQueue->Enqueue(p_bqr_event.release());
-}
-
-void ConfigureBqrCmpl(uint32_t current_evt_mask) {
-  LOG(INFO) << __func__ << ": current_evt_mask: " << loghex(current_evt_mask);
-  // (Un)Register for VSE of Bluetooth Quality Report sub event
-  tBTM_STATUS btm_status = BTM_BT_Quality_Report_VSE_Register(
-      current_evt_mask > kQualityEventMaskAllOff, AddBqrEventToQueue);
-
-  if (btm_status != BTM_SUCCESS) {
-    LOG(ERROR) << __func__ << ": Fail to (un)register VSE of BQR sub event."
-               << " status: " << btm_status;
-  }
-}
-
 void EnableBtQualityReport(bool is_enable) {
   LOG(INFO) << __func__ << ": is_enable: " << logbool(is_enable);
 
@@ -241,6 +256,31 @@
   ConfigureBqr(bqr_config);
 }
 
+void ConfigureBqr(const BqrConfiguration& bqr_config) {
+  if (bqr_config.report_action > REPORT_ACTION_CLEAR ||
+      bqr_config.quality_event_mask > kQualityEventMaskAll ||
+      bqr_config.minimum_report_interval_ms > kMinReportIntervalMaxMs) {
+    LOG(FATAL) << __func__ << ": Invalid Parameter"
+               << ", Action: " << bqr_config.report_action
+               << ", Mask: " << loghex(bqr_config.quality_event_mask)
+               << ", Interval: " << bqr_config.minimum_report_interval_ms;
+    return;
+  }
+
+  LOG(INFO) << __func__ << ": Action: " << bqr_config.report_action
+            << ", Mask: " << loghex(bqr_config.quality_event_mask)
+            << ", Interval: " << bqr_config.minimum_report_interval_ms;
+
+  uint8_t param[sizeof(BqrConfiguration)];
+  uint8_t* p_param = param;
+  UINT8_TO_STREAM(p_param, bqr_config.report_action);
+  UINT32_TO_STREAM(p_param, bqr_config.quality_event_mask);
+  UINT16_TO_STREAM(p_param, bqr_config.minimum_report_interval_ms);
+
+  BTM_VendorSpecificCommand(HCI_CONTROLLER_BQR, p_param - param, param,
+                            BqrVscCompleteCallback);
+}
+
 void BqrVscCompleteCallback(tBTM_VSC_CMPL* p_vsc_cmpl_params) {
   if (p_vsc_cmpl_params->param_len < 1) {
     LOG(ERROR) << __func__ << ": The length of returned parameters is less than 1";
@@ -273,29 +313,172 @@
   ConfigureBqrCmpl(current_quality_event_mask);
 }
 
-void ConfigureBqr(const BqrConfiguration& bqr_config) {
-  if (bqr_config.report_action > REPORT_ACTION_CLEAR ||
-      bqr_config.quality_event_mask > kQualityEventMaskAll ||
-      bqr_config.minimum_report_interval_ms > kMinReportIntervalMaxMs) {
-    LOG(FATAL) << __func__ << ": Invalid Parameter"
-               << ", Action: " << bqr_config.report_action
-               << ", Mask: " << loghex(bqr_config.quality_event_mask)
-               << ", Interval: " << bqr_config.minimum_report_interval_ms;
+void ConfigureBqrCmpl(uint32_t current_evt_mask) {
+  LOG(INFO) << __func__ << ": current_evt_mask: " << loghex(current_evt_mask);
+  // (Un)Register for VSE of Bluetooth Quality Report sub event
+  tBTM_STATUS btm_status = BTM_BT_Quality_Report_VSE_Register(
+      current_evt_mask > kQualityEventMaskAllOff, CategorizeBqrEvent);
+
+  if (btm_status != BTM_SUCCESS) {
+    LOG(ERROR) << __func__ << ": Fail to (un)register VSE of BQR sub event."
+               << " status: " << btm_status;
     return;
   }
 
-  LOG(INFO) << __func__ << ": Action: " << bqr_config.report_action
-            << ", Mask: " << loghex(bqr_config.quality_event_mask)
-            << ", Interval: " << bqr_config.minimum_report_interval_ms;
+  if (LmpLlMessageTraceLogFd != INVALID_FD &&
+      (current_evt_mask & kQualityEventMaskLmpMessageTrace) == 0) {
+    LOG(INFO) << __func__ << ": Closing LMP/LL log file.";
+    close(LmpLlMessageTraceLogFd);
+    LmpLlMessageTraceLogFd = INVALID_FD;
+  }
+  if (BtSchedulingTraceLogFd != INVALID_FD &&
+      (current_evt_mask & kQualityEventMaskBtSchedulingTrace) == 0) {
+    LOG(INFO) << __func__ << ": Closing Scheduling log file.";
+    close(BtSchedulingTraceLogFd);
+    BtSchedulingTraceLogFd = INVALID_FD;
+  }
+}
 
-  uint8_t param[sizeof(BqrConfiguration)];
-  uint8_t* p_param = param;
-  UINT8_TO_STREAM(p_param, bqr_config.report_action);
-  UINT32_TO_STREAM(p_param, bqr_config.quality_event_mask);
-  UINT16_TO_STREAM(p_param, bqr_config.minimum_report_interval_ms);
+void CategorizeBqrEvent(uint8_t length, uint8_t* p_bqr_event) {
+  if (length == 0) {
+    LOG(WARNING) << __func__ << ": Lengths of all of the parameters are zero.";
+    return;
+  }
 
-  BTM_VendorSpecificCommand(HCI_CONTROLLER_BQR, p_param - param,
-                            param, BqrVscCompleteCallback);
+  uint8_t quality_report_id = p_bqr_event[0];
+  switch (quality_report_id) {
+    case QUALITY_REPORT_ID_MONITOR_MODE:
+    case QUALITY_REPORT_ID_APPROACH_LSTO:
+    case QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY:
+    case QUALITY_REPORT_ID_SCO_VOICE_CHOPPY:
+      if (length < kLinkQualityParamTotalLen) {
+        LOG(FATAL) << __func__
+                   << ": Parameter total length: " << std::to_string(length)
+                   << " is abnormal. It shall be not shorter than: "
+                   << std::to_string(kLinkQualityParamTotalLen);
+        return;
+      }
+
+      AddLinkQualityEventToQueue(length, p_bqr_event);
+      break;
+
+    // The Root Inflammation and Log Dump related event should be handled and
+    // intercepted already.
+    case QUALITY_REPORT_ID_ROOT_INFLAMMATION:
+    case QUALITY_REPORT_ID_LMP_LL_MESSAGE_TRACE:
+    case QUALITY_REPORT_ID_BT_SCHEDULING_TRACE:
+    case QUALITY_REPORT_ID_CONTROLLER_DBG_INFO:
+      LOG(WARNING) << __func__
+                   << ": Unexpected ID: " << loghex(quality_report_id);
+      break;
+
+    default:
+      LOG(WARNING) << __func__ << ": Unknown ID: " << loghex(quality_report_id);
+      break;
+  }
+}
+
+void AddLinkQualityEventToQueue(uint8_t length, uint8_t* p_link_quality_event) {
+  std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
+  p_bqr_event->ParseBqrLinkQualityEvt(length, p_link_quality_event);
+
+  LOG(WARNING) << *p_bqr_event;
+  int ret = android::util::stats_write(
+      android::util::BLUETOOTH_QUALITY_REPORT_REPORTED,
+      p_bqr_event->bqr_link_quality_event_.quality_report_id,
+      p_bqr_event->bqr_link_quality_event_.packet_types,
+      p_bqr_event->bqr_link_quality_event_.connection_handle,
+      p_bqr_event->bqr_link_quality_event_.connection_role,
+      p_bqr_event->bqr_link_quality_event_.tx_power_level,
+      p_bqr_event->bqr_link_quality_event_.rssi,
+      p_bqr_event->bqr_link_quality_event_.snr,
+      p_bqr_event->bqr_link_quality_event_.unused_afh_channel_count,
+      p_bqr_event->bqr_link_quality_event_.afh_select_unideal_channel_count,
+      p_bqr_event->bqr_link_quality_event_.lsto,
+      p_bqr_event->bqr_link_quality_event_.connection_piconet_clock,
+      p_bqr_event->bqr_link_quality_event_.retransmission_count,
+      p_bqr_event->bqr_link_quality_event_.no_rx_count,
+      p_bqr_event->bqr_link_quality_event_.nak_count,
+      p_bqr_event->bqr_link_quality_event_.last_tx_ack_timestamp,
+      p_bqr_event->bqr_link_quality_event_.flow_off_count,
+      p_bqr_event->bqr_link_quality_event_.last_flow_on_timestamp,
+      p_bqr_event->bqr_link_quality_event_.buffer_overflow_bytes,
+      p_bqr_event->bqr_link_quality_event_.buffer_underflow_bytes);
+  if (ret < 0) {
+    LOG(WARNING) << __func__ << ": failed to log BQR event to statsd, error "
+                 << ret;
+  }
+  kpBqrEventQueue->Enqueue(p_bqr_event.release());
+}
+
+void DumpLmpLlMessage(uint8_t length, uint8_t* p_lmp_ll_message_event) {
+  std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
+
+  if (LmpLlMessageTraceLogFd == INVALID_FD ||
+      LmpLlMessageTraceCounter >= kLogDumpEventPerFile) {
+    LmpLlMessageTraceLogFd = OpenLmpLlTraceLogFile();
+  }
+  if (LmpLlMessageTraceLogFd != INVALID_FD) {
+    p_bqr_event->WriteLmpLlTraceLogFile(LmpLlMessageTraceLogFd, length,
+                                        p_lmp_ll_message_event);
+  }
+}
+
+int OpenLmpLlTraceLogFile() {
+  if (rename(kpLmpLlMessageTraceLogPath, kpLmpLlMessageTraceLastLogPath) != 0 &&
+      errno != ENOENT) {
+    LOG(ERROR) << __func__ << ": Unable to rename '"
+               << kpLmpLlMessageTraceLogPath << "' to '"
+               << kpLmpLlMessageTraceLastLogPath << "' : " << strerror(errno);
+  }
+
+  mode_t prevmask = umask(0);
+  int logfile_fd =
+      open(kpLmpLlMessageTraceLogPath, O_WRONLY | O_CREAT | O_TRUNC,
+           S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+  umask(prevmask);
+  if (logfile_fd == INVALID_FD) {
+    LOG(ERROR) << __func__ << ": Unable to open '" << kpLmpLlMessageTraceLogPath
+               << "' : " << strerror(errno);
+  } else {
+    LmpLlMessageTraceCounter = 0;
+  }
+  return logfile_fd;
+}
+
+void DumpBtScheduling(uint8_t length, uint8_t* p_bt_scheduling_event) {
+  std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
+
+  if (BtSchedulingTraceLogFd == INVALID_FD ||
+      BtSchedulingTraceCounter == kLogDumpEventPerFile) {
+    BtSchedulingTraceLogFd = OpenBtSchedulingTraceLogFile();
+  }
+  if (BtSchedulingTraceLogFd != INVALID_FD) {
+    p_bqr_event->WriteBtSchedulingTraceLogFile(BtSchedulingTraceLogFd, length,
+                                               p_bt_scheduling_event);
+  }
+}
+
+int OpenBtSchedulingTraceLogFile() {
+  if (rename(kpBtSchedulingTraceLogPath, kpBtSchedulingTraceLastLogPath) != 0 &&
+      errno != ENOENT) {
+    LOG(ERROR) << __func__ << ": Unable to rename '"
+               << kpBtSchedulingTraceLogPath << "' to '"
+               << kpBtSchedulingTraceLastLogPath << "' : " << strerror(errno);
+  }
+
+  mode_t prevmask = umask(0);
+  int logfile_fd =
+      open(kpBtSchedulingTraceLogPath, O_WRONLY | O_CREAT | O_TRUNC,
+           S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+  umask(prevmask);
+  if (logfile_fd == INVALID_FD) {
+    LOG(ERROR) << __func__ << ": Unable to open '" << kpBtSchedulingTraceLogPath
+               << "' : " << strerror(errno);
+  } else {
+    BtSchedulingTraceCounter = 0;
+  }
+  return logfile_fd;
 }
 
 void DebugDump(int fd) {
@@ -309,8 +492,9 @@
   while (!kpBqrEventQueue->Empty()) {
     std::unique_ptr<BqrVseSubEvt> p_event(kpBqrEventQueue->Dequeue());
 
-    bool warning = (p_event->rssi_ < kCriWarnRssi ||
-                    p_event->unused_afh_channel_count_ > kCriWarnUnusedCh);
+    bool warning = (p_event->bqr_link_quality_event_.rssi < kCriWarnRssi ||
+                    p_event->bqr_link_quality_event_.unused_afh_channel_count >
+                        kCriWarnUnusedCh);
 
     std::stringstream ss_timestamp;
     ss_timestamp << std::put_time(&p_event->tm_timestamp_, "%m-%d %H:%M:%S");
diff --git a/btif/src/btif_config.cc b/btif/src/btif_config.cc
index b1af0ba..37db243 100644
--- a/btif/src/btif_config.cc
+++ b/btif/src/btif_config.cc
@@ -20,27 +20,33 @@
 
 #include "btif_config.h"
 
-#include <base/logging.h>
-#include <ctype.h>
-#include <openssl/rand.h>
-#include <openssl/sha.h>
-#include <private/android_filesystem_config.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
 #include <unistd.h>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <ctime>
+#include <functional>
 #include <mutex>
 #include <sstream>
 #include <string>
+#include <unordered_map>
 
-#include "bt_types.h"
+#include <private/android_filesystem_config.h>
+
+#include <openssl/rand.h>
+
+#include <base/logging.h>
+
 #include "btcore/include/module.h"
 #include "btif_api.h"
 #include "btif_common.h"
+#include "btif_config_cache.h"
 #include "btif_config_transcode.h"
-//#include "btif_keystore.h"
-#include "btif_util.h"
+#include "btif_keystore.h"
 #include "common/address_obfuscator.h"
+#include "common/metric_id_allocator.h"
+#include "main/shim/config.h"
+#include "main/shim/shim.h"
 #include "osi/include/alarm.h"
 #include "osi/include/allocator.h"
 #include "osi/include/compat.h"
@@ -48,8 +54,10 @@
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 #include "osi/include/properties.h"
+#include "raw_address.h"
 
 #define BT_CONFIG_SOURCE_TAG_NUM 1010001
+#define TEMPORARY_SECTION_CAPACITY 10000
 
 #define INFO_SECTION "Info"
 #define FILE_TIMESTAMP "TimeCreated"
@@ -58,16 +66,13 @@
 #define DISABLED "disabled"
 static const char* TIME_STRING_FORMAT = "%Y-%m-%d %H:%M:%S";
 
-// constexpr int kBufferSize = 400 * 10;  // initial file is ~400B
-
-/*static bool use_key_attestation() {
-  return getuid() == AID_BLUETOOTH && is_single_user_mode();
-}*/
-
 #define BT_CONFIG_METRICS_SECTION "Metrics"
 #define BT_CONFIG_METRICS_SALT_256BIT "Salt256Bit"
-// using bluetooth::BtifKeystore;
+#define BT_CONFIG_METRICS_ID_KEY "MetricsId"
+
+using bluetooth::bluetooth_keystore::BluetoothKeystoreInterface;
 using bluetooth::common::AddressObfuscator;
+using bluetooth::common::MetricIdAllocator;
 
 // TODO(armansito): Find a better way than searching by a hardcoded path.
 #if defined(OS_GENERIC)
@@ -77,8 +82,6 @@
 #else   // !defined(OS_GENERIC)
 static const char* CONFIG_FILE_PATH = "/data/misc/bluedroid/bt_config.conf";
 static const char* CONFIG_BACKUP_PATH = "/data/misc/bluedroid/bt_config.bak";
-static const char* CONFIG_FILE_CHECKSUM_PATH = "/data/misc/bluedroid/bt_config.conf.encrypted-checksum";
-static const char* CONFIG_BACKUP_CHECKSUM_PATH = "/data/misc/bluedroid/bt_config.bak.encrypted-checksum";
 static const char* CONFIG_LEGACY_FILE_PATH =
     "/data/misc/bluedroid/bt_config.xml";
 #endif  // defined(OS_GENERIC)
@@ -88,15 +91,26 @@
 static void btif_config_write(uint16_t event, char* p_param);
 static bool is_factory_reset(void);
 static void delete_config_files(void);
-static void btif_config_remove_unpaired(config_t* config);
-static void btif_config_remove_restricted(config_t* config);
-static std::unique_ptr<config_t> btif_config_open(const char* filename, const char* checksum_filename);
+static std::unique_ptr<config_t> btif_config_open(const char* filename);
 
 // Key attestation
-// static std::string hash_file(const char* filename);
-// static std::string read_checksum_file(const char* filename);
-// static void write_checksum_file(const char* filename, const std::string&
-// hash);
+static bool config_checksum_pass(int check_bit) {
+  return ((get_niap_config_compare_result() & check_bit) == check_bit);
+}
+static bool btif_is_niap_mode() {
+  return getuid() == AID_BLUETOOTH && is_niap_mode();
+}
+static bool btif_in_encrypt_key_name_list(std::string key);
+
+static const int CONFIG_FILE_COMPARE_PASS = 1;
+static const int CONFIG_BACKUP_COMPARE_PASS = 2;
+static const std::string ENCRYPTED_STR = "encrypted";
+static const std::string CONFIG_FILE_PREFIX = "bt_config-origin";
+static const std::string CONFIG_FILE_HASH = "hash";
+static const int ENCRYPT_KEY_NAME_LIST_SIZE = 7;
+static const std::string encrypt_key_name_list[] = {
+    "LinkKey",      "LE_KEY_PENC", "LE_KEY_PID",  "LE_KEY_LID",
+    "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"};
 
 static enum ConfigSource {
   NOT_LOADED,
@@ -107,9 +121,12 @@
   RESET
 } btif_config_source = NOT_LOADED;
 
-static int btif_config_devices_loaded = -1;
 static char btif_config_time_created[TIME_STRING_LENGTH];
 
+static BluetoothKeystoreInterface* get_bluetooth_keystore_interface() {
+  return bluetooth::bluetooth_keystore::getBluetoothKeystoreInterface();
+}
+
 // TODO(zachoverflow): Move these two functions out, because they are too
 // specific for this file
 // {grumpy-cat/no, monty-python/you-make-me-sad}
@@ -121,8 +138,7 @@
 
   if (!btif_config_get_int(bd_addr_str, "DevType", p_device_type)) return false;
 
-  LOG_DEBUG(LOG_TAG, "%s: Device [%s] type %d", __func__, bd_addr_str,
-            *p_device_type);
+  LOG_DEBUG("%s: Device [%s] type %d", __func__, bd_addr_str, *p_device_type);
   return true;
 }
 
@@ -134,7 +150,7 @@
 
   if (!btif_config_get_int(bd_addr_str, "AddrType", p_addr_type)) return false;
 
-  LOG_DEBUG(LOG_TAG, "%s: Device [%s] address type %d", __func__, bd_addr_str,
+  LOG_DEBUG("%s: Device [%s] address type %d", __func__, bd_addr_str,
             *p_addr_type);
   return true;
 }
@@ -173,83 +189,147 @@
   AddressObfuscator::GetInstance()->Initialize(metrics_salt);
 }
 
+/**
+ * Initialize metric id allocator by reading metric_id from config by mac
+ * address. If there is no metric id for a mac address, then allocate it a new
+ * metric id.
+ */
+static void init_metric_id_allocator() {
+  std::unordered_map<RawAddress, int> paired_device_map;
+
+  // When user update the system, there will be devices paired with older
+  // version of android without a metric id.
+  std::vector<RawAddress> addresses_without_id;
+
+  for (const auto& mac_address : btif_config_get_paired_devices()) {
+    auto addr_str = mac_address.ToString();
+    // if the section name is a mac address
+    bool is_valid_id_found = false;
+    if (btif_config_exist(addr_str, BT_CONFIG_METRICS_ID_KEY)) {
+      // there is one metric id under this mac_address
+      int id = 0;
+      btif_config_get_int(addr_str, BT_CONFIG_METRICS_ID_KEY, &id);
+      if (MetricIdAllocator::IsValidId(id)) {
+        paired_device_map[mac_address] = id;
+        is_valid_id_found = true;
+      }
+    }
+    if (!is_valid_id_found) {
+      addresses_without_id.push_back(mac_address);
+    }
+  }
+
+  // Initialize MetricIdAllocator
+  MetricIdAllocator::Callback save_device_callback =
+      [](const RawAddress& address, const int id) {
+        return btif_config_set_int(address.ToString(), BT_CONFIG_METRICS_ID_KEY,
+                                   id);
+      };
+  MetricIdAllocator::Callback forget_device_callback =
+      [](const RawAddress& address, const int id) {
+        return btif_config_remove(address.ToString(), BT_CONFIG_METRICS_ID_KEY);
+      };
+  if (!MetricIdAllocator::GetInstance().Init(
+          paired_device_map, std::move(save_device_callback),
+          std::move(forget_device_callback))) {
+    LOG(FATAL) << __func__ << "Failed to initialize MetricIdAllocator";
+  }
+
+  // Add device_without_id
+  for (auto& address : addresses_without_id) {
+    MetricIdAllocator::GetInstance().AllocateId(address);
+    MetricIdAllocator::GetInstance().SaveDevice(address);
+  }
+}
+
 static std::recursive_mutex config_lock;  // protects operations on |config|.
-static std::unique_ptr<config_t> config;
 static alarm_t* config_timer;
 
-// static BtifKeystore btif_keystore(new keystore::KeystoreClientImpl);
+// limited btif config cache capacity
+static BtifConfigCache btif_config_cache(TEMPORARY_SECTION_CAPACITY);
 
 // Module lifecycle functions
 
 static future_t* init(void) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    // TODO (b/158035889) Migrate metrics module to GD
+    read_or_set_metrics_salt();
+    init_metric_id_allocator();
+    return future_new_immediate(FUTURE_SUCCESS);
+  }
   std::unique_lock<std::recursive_mutex> lock(config_lock);
+  std::unique_ptr<config_t> config;
 
   if (is_factory_reset()) delete_config_files();
-  /*if (is_factory_reset() ||
-      (use_key_attestation() && !btif_keystore.DoesKeyExist()))
-    delete_config_files();*/
 
   std::string file_source;
 
-  config = btif_config_open(CONFIG_FILE_PATH, CONFIG_FILE_CHECKSUM_PATH);
-  btif_config_source = ORIGINAL;
-  if (!config) {
-    LOG_WARN(LOG_TAG, "%s unable to load config file: %s; using backup.",
-             __func__, CONFIG_FILE_PATH);
-    remove(CONFIG_FILE_CHECKSUM_PATH);
-    config = btif_config_open(CONFIG_BACKUP_PATH, CONFIG_BACKUP_CHECKSUM_PATH);
-    btif_config_source = BACKUP;
-    file_source = "Backup";
+  if (config_checksum_pass(CONFIG_FILE_COMPARE_PASS)) {
+    config = btif_config_open(CONFIG_FILE_PATH);
+    btif_config_source = ORIGINAL;
   }
   if (!config) {
-    LOG_WARN(LOG_TAG,
-             "%s unable to load backup; attempting to transcode legacy file.",
+    LOG_WARN("%s unable to load config file: %s; using backup.", __func__,
+             CONFIG_FILE_PATH);
+    if (config_checksum_pass(CONFIG_BACKUP_COMPARE_PASS)) {
+      config = btif_config_open(CONFIG_BACKUP_PATH);
+      btif_config_source = BACKUP;
+      file_source = "Backup";
+    }
+  }
+  if (!config) {
+    LOG_WARN("%s unable to load backup; attempting to transcode legacy file.",
              __func__);
-    remove(CONFIG_BACKUP_CHECKSUM_PATH);
     config = btif_config_transcode(CONFIG_LEGACY_FILE_PATH);
     btif_config_source = LEGACY;
     file_source = "Legacy";
   }
   if (!config) {
-    LOG_ERROR(LOG_TAG,
-              "%s unable to transcode legacy file; creating empty config.",
+    LOG_ERROR("%s unable to transcode legacy file; creating empty config.",
               __func__);
     config = config_new_empty();
     btif_config_source = NEW_FILE;
     file_source = "Empty";
   }
 
-  if (!file_source.empty())
-    config_set_string(config.get(), INFO_SECTION, FILE_SOURCE, file_source);
+  // move persistent config data from btif_config file to btif config cache
+  btif_config_cache.Init(std::move(config));
 
-  btif_config_remove_unpaired(config.get());
+  if (!file_source.empty()) {
+    btif_config_cache.SetString(INFO_SECTION, FILE_SOURCE, file_source);
+  }
 
   // Cleanup temporary pairings if we have left guest mode
-  if (!is_restricted_mode()) btif_config_remove_restricted(config.get());
+  if (!is_restricted_mode()) {
+    btif_config_cache.RemovePersistentSectionsWithKey("Restricted");
+  }
 
   // Read or set config file creation timestamp
-  const std::string* time_str;
-  time_str = config_get_string(*config, INFO_SECTION, FILE_TIMESTAMP, NULL);
-  if (time_str != NULL) {
-    strlcpy(btif_config_time_created, time_str->c_str(), TIME_STRING_LENGTH);
-  } else {
+  auto time_str = btif_config_cache.GetString(INFO_SECTION, FILE_TIMESTAMP);
+  if (!time_str) {
     time_t current_time = time(NULL);
     struct tm* time_created = localtime(&current_time);
     strftime(btif_config_time_created, TIME_STRING_LENGTH, TIME_STRING_FORMAT,
              time_created);
-    config_set_string(config.get(), INFO_SECTION, FILE_TIMESTAMP,
-                      btif_config_time_created);
+    btif_config_cache.SetString(INFO_SECTION, FILE_TIMESTAMP,
+                                btif_config_time_created);
+  } else {
+    strlcpy(btif_config_time_created, time_str->c_str(), TIME_STRING_LENGTH);
   }
 
   // Read or set metrics 256 bit hashing salt
   read_or_set_metrics_salt();
 
+  // Initialize MetricIdAllocator
+  init_metric_id_allocator();
+
   // TODO(sharvil): use a non-wake alarm for this once we have
   // API support for it. There's no need to wake the system to
   // write back to disk.
   config_timer = alarm_new("btif.config");
   if (!config_timer) {
-    LOG_ERROR(LOG_TAG, "%s unable to create alarm.", __func__);
+    LOG_ERROR("%s unable to create alarm.", __func__);
     goto error;
   }
 
@@ -260,35 +340,18 @@
 error:
   alarm_free(config_timer);
   config.reset();
+  btif_config_cache.Clear();
   config_timer = NULL;
   btif_config_source = NOT_LOADED;
   return future_new_immediate(FUTURE_FAIL);
 }
 
-static std::unique_ptr<config_t> btif_config_open(const char* filename, const char* checksum_filename) {
-  /*// START KEY ATTESTATION
-  // Get hash of current file
-  std::string current_hash = hash_file(filename);
-  // Get stored hash
-  std::string stored_hash = read_checksum_file(checksum_filename);
-  if (stored_hash.empty()) {
-    LOG(ERROR) << __func__ << ": stored_hash=<empty>";
-    if (!current_hash.empty()) {
-      write_checksum_file(checksum_filename, current_hash);
-      stored_hash = read_checksum_file(checksum_filename);
-    }
-  }
-  // Compare hashes
-  if (current_hash != stored_hash) {
-    return nullptr;
-  }
-  // END KEY ATTESTATION*/
-
+static std::unique_ptr<config_t> btif_config_open(const char* filename) {
   std::unique_ptr<config_t> config = config_new(filename);
   if (!config) return nullptr;
 
   if (!config_has_section(*config, "Adapter")) {
-    LOG_ERROR(LOG_TAG, "Config is missing adapter section");
+    LOG_ERROR("Config is missing adapter section");
     return nullptr;
   }
 
@@ -301,13 +364,22 @@
 }
 
 static future_t* clean_up(void) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    // GD storage module cleanup by itself
+    std::unique_lock<std::recursive_mutex> lock(config_lock);
+    MetricIdAllocator::GetInstance().Close();
+    return future_new_immediate(FUTURE_SUCCESS);
+  }
   btif_config_flush();
 
   alarm_free(config_timer);
   config_timer = NULL;
 
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  config.reset();
+  get_bluetooth_keystore_interface()->clear_map();
+  MetricIdAllocator::GetInstance().Close();
+  btif_config_cache.Clear();
   return future_new_immediate(FUTURE_SUCCESS);
 }
 
@@ -317,107 +389,138 @@
                                              .shut_down = shut_down,
                                              .clean_up = clean_up};
 
-bool btif_config_has_section(const char* section) {
-  CHECK(config != NULL);
-  CHECK(section != NULL);
-
-  std::unique_lock<std::recursive_mutex> lock(config_lock);
-  return config_has_section(*config, section);
-}
-
 bool btif_config_exist(const std::string& section, const std::string& key) {
-  CHECK(config != NULL);
-
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::HasProperty(section, key);
+  }
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  return config_has_key(*config, section, key);
+  return btif_config_cache.HasKey(section, key);
 }
 
 bool btif_config_get_int(const std::string& section, const std::string& key,
                          int* value) {
-  CHECK(config != NULL);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::GetInt(section, key, value);
+  }
   CHECK(value != NULL);
-
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  bool ret = config_has_key(*config, section, key);
-  if (ret) *value = config_get_int(*config, section, key, *value);
-
-  return ret;
+  auto ret = btif_config_cache.GetInt(section, key);
+  if (!ret) {
+    return false;
+  }
+  *value = *ret;
+  return true;
 }
 
 bool btif_config_set_int(const std::string& section, const std::string& key,
                          int value) {
-  CHECK(config != NULL);
-
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::SetInt(section, key, value);
+  }
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  config_set_int(config.get(), section, key, value);
-
+  btif_config_cache.SetInt(section, key, value);
   return true;
 }
 
 bool btif_config_get_uint64(const std::string& section, const std::string& key,
                             uint64_t* value) {
-  CHECK(config != NULL);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::GetUint64(section, key, value);
+  }
   CHECK(value != NULL);
-
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  bool ret = config_has_key(*config, section, key);
-  if (ret) *value = config_get_uint64(*config, section, key, *value);
-
-  return ret;
+  auto ret = btif_config_cache.GetUint64(section, key);
+  if (!ret) {
+    return false;
+  }
+  *value = *ret;
+  return true;
 }
 
 bool btif_config_set_uint64(const std::string& section, const std::string& key,
                             uint64_t value) {
-  CHECK(config != NULL);
-
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::SetUint64(section, key, value);
+  }
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  config_set_uint64(config.get(), section, key, value);
-
+  btif_config_cache.SetUint64(section, key, value);
   return true;
 }
 
 bool btif_config_get_str(const std::string& section, const std::string& key,
                          char* value, int* size_bytes) {
-  CHECK(config != NULL);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::GetStr(section, key, value,
+                                                        size_bytes);
+  }
   CHECK(value != NULL);
   CHECK(size_bytes != NULL);
 
   {
     std::unique_lock<std::recursive_mutex> lock(config_lock);
-    const std::string* stored_value =
-        config_get_string(*config, section, key, NULL);
+    auto stored_value = btif_config_cache.GetString(section, key);
     if (!stored_value) return false;
     strlcpy(value, stored_value->c_str(), *size_bytes);
   }
-
   *size_bytes = strlen(value) + 1;
   return true;
 }
 
 bool btif_config_set_str(const std::string& section, const std::string& key,
                          const std::string& value) {
-  CHECK(config != NULL);
-
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::SetStr(section, key, value);
+  }
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  config_set_string(config.get(), section, key, value);
+  btif_config_cache.SetString(section, key, value);
   return true;
 }
 
+static bool btif_in_encrypt_key_name_list(std::string key) {
+  return std::find(encrypt_key_name_list,
+                   encrypt_key_name_list + ENCRYPT_KEY_NAME_LIST_SIZE,
+                   key) != (encrypt_key_name_list + ENCRYPT_KEY_NAME_LIST_SIZE);
+}
+
 bool btif_config_get_bin(const std::string& section, const std::string& key,
                          uint8_t* value, size_t* length) {
-  CHECK(config != NULL);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::GetBin(section, key, value,
+                                                        length);
+  }
   CHECK(value != NULL);
   CHECK(length != NULL);
 
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  const std::string* value_str = config_get_string(*config, section, key, NULL);
+  const std::string* value_str;
+  auto value_str_from_config = btif_config_cache.GetString(section, key);
 
-  if (!value_str) {
+  if (!value_str_from_config) {
     VLOG(1) << __func__ << ": cannot find string for section " << section
             << ", key " << key;
     return false;
   }
 
+  bool in_encrypt_key_name_list = btif_in_encrypt_key_name_list(key);
+  bool is_key_encrypted = *value_str_from_config == ENCRYPTED_STR;
+  std::string string;
+
+  if (!value_str_from_config->empty() && in_encrypt_key_name_list &&
+      is_key_encrypted) {
+    string = get_bluetooth_keystore_interface()->get_key(section + "-" + key);
+    value_str = &string;
+  } else {
+    value_str = &value_str_from_config.value();
+  }
+
   size_t value_len = value_str->length();
   if ((value_len % 2) != 0 || *length < (value_len / 2)) {
     LOG(WARNING) << ": value size not divisible by 2, size is " << value_len;
@@ -431,30 +534,47 @@
     }
 
   const char* ptr = value_str->c_str();
-  for (*length = 0; *ptr; ptr += 2, *length += 1)
+  for (*length = 0; *ptr; ptr += 2, *length += 1) {
     sscanf(ptr, "%02hhx", &value[*length]);
+  }
+
+  if (btif_is_niap_mode()) {
+    if (!value_str_from_config->empty() && in_encrypt_key_name_list &&
+        !is_key_encrypted) {
+      get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key(
+          section + "-" + key, *value_str_from_config);
+      btif_config_cache.SetString(section, key, ENCRYPTED_STR);
+    }
+  } else {
+    if (in_encrypt_key_name_list && is_key_encrypted) {
+      btif_config_cache.SetString(section, key, *value_str);
+    }
+  }
 
   return true;
 }
 
 size_t btif_config_get_bin_length(const std::string& section,
                                   const std::string& key) {
-  CHECK(config != NULL);
-
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::GetBinLength(section, key);
+  }
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  const std::string* value_str = config_get_string(*config, section, key, NULL);
+  auto value_str = btif_config_cache.GetString(section, key);
   if (!value_str) return 0;
-
   size_t value_len = value_str->length();
   return ((value_len % 2) != 0) ? 0 : (value_len / 2);
 }
 
 bool btif_config_set_bin(const std::string& section, const std::string& key,
                          const uint8_t* value, size_t length) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::SetBin(section, key, value,
+                                                        length);
+  }
   const char* lookup = "0123456789abcdef";
-
-  CHECK(config != NULL);
-
   if (length > 0) CHECK(value != NULL);
 
   size_t max_value = ((size_t)-1);
@@ -470,33 +590,77 @@
     str[(i * 2) + 1] = lookup[value[i] & 0x0F];
   }
 
+  std::string value_str;
+  if ((length > 0) && btif_is_niap_mode() &&
+      btif_in_encrypt_key_name_list(key)) {
+    get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key(
+        section + "-" + key, str);
+    value_str = ENCRYPTED_STR;
+  } else {
+    value_str = str;
+  }
+
   {
     std::unique_lock<std::recursive_mutex> lock(config_lock);
-    config_set_string(config.get(), section, key, str);
+    btif_config_cache.SetString(section, key, value_str);
   }
 
   osi_free(str);
   return true;
 }
 
-std::list<section_t>& btif_config_sections() { return config->sections; }
+std::vector<RawAddress> btif_config_get_paired_devices() {
+  std::vector<std::string> names;
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    names = bluetooth::shim::BtifConfigInterface::GetPersistentDevices();
+  } else {
+    std::unique_lock<std::recursive_mutex> lock(config_lock);
+    names = btif_config_cache.GetPersistentSectionNames();
+  }
+  std::vector<RawAddress> result;
+  result.reserve(names.size());
+  for (const auto& name : names) {
+    RawAddress addr = {};
+    if (!RawAddress::FromString(name, addr)) {
+      LOG(WARNING) << __func__ << ": " << name << " is not a valid address";
+      continue;
+    }
+    result.emplace_back(addr);
+  }
+  return result;
+}
 
 bool btif_config_remove(const std::string& section, const std::string& key) {
-  CHECK(config != NULL);
-
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    return bluetooth::shim::BtifConfigInterface::RemoveProperty(section, key);
+  }
+  if (is_niap_mode() && btif_in_encrypt_key_name_list(key)) {
+    get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key(
+        section + "-" + key, "");
+  }
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  return config_remove_key(config.get(), section, key);
+  return btif_config_cache.RemoveKey(section, key);
 }
 
 void btif_config_save(void) {
-  CHECK(config != NULL);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    bluetooth::shim::BtifConfigInterface::Save();
+    return;
+  }
   CHECK(config_timer != NULL);
 
   alarm_set(config_timer, CONFIG_SETTLE_PERIOD_MS, timer_config_save_cb, NULL);
 }
 
 void btif_config_flush(void) {
-  CHECK(config != NULL);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    bluetooth::shim::BtifConfigInterface::Flush();
+    return;
+  }
   CHECK(config_timer != NULL);
 
   alarm_cancel(config_timer);
@@ -504,24 +668,23 @@
 }
 
 bool btif_config_clear(void) {
-  CHECK(config != NULL);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    CHECK(bluetooth::shim::is_gd_stack_started_up());
+    bluetooth::shim::BtifConfigInterface::Clear();
+    bluetooth::shim::BtifConfigInterface::Save();
+    return true;
+  }
   CHECK(config_timer != NULL);
 
   alarm_cancel(config_timer);
 
   std::unique_lock<std::recursive_mutex> lock(config_lock);
 
-  config = config_new_empty();
-
-  bool ret = config_save(*config, CONFIG_FILE_PATH);
+  btif_config_cache.Clear();
+  bool ret =
+      config_save(btif_config_cache.PersistentSectionCopy(), CONFIG_FILE_PATH);
   btif_config_source = RESET;
 
-  /*// Save encrypted hash
-  std::string current_hash = hash_file(CONFIG_FILE_PATH);
-  if (!current_hash.empty()) {
-    write_checksum_file(CONFIG_FILE_CHECKSUM_PATH, current_hash);
-  }*/
-
   return ret;
 }
 
@@ -534,51 +697,15 @@
 
 static void btif_config_write(UNUSED_ATTR uint16_t event,
                               UNUSED_ATTR char* p_param) {
-  CHECK(config != NULL);
   CHECK(config_timer != NULL);
 
   std::unique_lock<std::recursive_mutex> lock(config_lock);
   rename(CONFIG_FILE_PATH, CONFIG_BACKUP_PATH);
-  rename(CONFIG_FILE_CHECKSUM_PATH, CONFIG_BACKUP_CHECKSUM_PATH);
-  std::unique_ptr<config_t> config_paired = config_new_clone(*config);
-  btif_config_remove_unpaired(config_paired.get());
-  config_save(*config_paired, CONFIG_FILE_PATH);
-  /*// Save hash
-  std::string current_hash = hash_file(CONFIG_FILE_PATH);
-  if (!current_hash.empty()) {
-    write_checksum_file(CONFIG_FILE_CHECKSUM_PATH, current_hash);
-  }*/
-}
-
-static void btif_config_remove_unpaired(config_t* conf) {
-  CHECK(conf != NULL);
-  int paired_devices = 0;
-
-  // The paired config used to carry information about
-  // discovered devices during regular inquiry scans.
-  // We remove these now and cache them in memory instead.
-  for (auto it = conf->sections.begin(); it != conf->sections.end();) {
-    std::string& section = it->name;
-    if (RawAddress::IsValidAddress(section)) {
-      // TODO: config_has_key loop thorugh all data, maybe just make it so we
-      // loop just once ?
-      if (!config_has_key(*conf, section, "LinkKey") &&
-          !config_has_key(*conf, section, "LE_KEY_PENC") &&
-          !config_has_key(*conf, section, "LE_KEY_PID") &&
-          !config_has_key(*conf, section, "LE_KEY_PCSRK") &&
-          !config_has_key(*conf, section, "LE_KEY_LENC") &&
-          !config_has_key(*conf, section, "LE_KEY_LCSRK")) {
-        it = conf->sections.erase(it);
-        continue;
-      }
-      paired_devices++;
-    }
-    it++;
+  config_save(btif_config_cache.PersistentSectionCopy(), CONFIG_FILE_PATH);
+  if (btif_is_niap_mode()) {
+    get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key(
+        CONFIG_FILE_PREFIX, CONFIG_FILE_HASH);
   }
-
-  // should only happen once, at initial load time
-  if (btif_config_devices_loaded == -1)
-    btif_config_devices_loaded = paired_devices;
 }
 
 void btif_debug_config_dump(int fd) {
@@ -606,28 +733,21 @@
       break;
   }
 
-  std::string original = "Original";
-  dprintf(fd, "  Devices loaded: %d\n", btif_config_devices_loaded);
-  dprintf(fd, "  File created/tagged: %s\n", btif_config_time_created);
-  dprintf(fd, "  File source: %s\n",
-          config_get_string(*config, INFO_SECTION, FILE_SOURCE, &original)
-              ->c_str());
-}
-
-static void btif_config_remove_restricted(config_t* config) {
-  CHECK(config != NULL);
-
-  for (auto it = config->sections.begin(); it != config->sections.end();) {
-    const std::string& section = it->name;
-    if (RawAddress::IsValidAddress(section) &&
-        config_has_key(*config, section, "Restricted")) {
-      BTIF_TRACE_DEBUG("%s: Removing restricted device %s", __func__,
-                       section.c_str());
-      it = config->sections.erase(it);
-      continue;
-    }
-    it++;
+  std::optional<std::string> file_source;
+  if (bluetooth::shim::is_gd_stack_started_up()) {
+    CHECK(bluetooth::shim::is_gd_shim_enabled());
+    file_source =
+        bluetooth::shim::BtifConfigInterface::GetStr(INFO_SECTION, FILE_SOURCE);
+  } else {
+    file_source = btif_config_cache.GetString(INFO_SECTION, FILE_SOURCE);
   }
+  if (!file_source) {
+    file_source.emplace("Original");
+  }
+  auto devices = btif_config_cache.GetPersistentSectionNames();
+  dprintf(fd, "  Devices loaded: %zu\n", devices.size());
+  dprintf(fd, "  File created/tagged: %s\n", btif_config_time_created);
+  dprintf(fd, "  File source: %s\n", file_source->c_str());
 }
 
 static bool is_factory_reset(void) {
@@ -639,65 +759,5 @@
 static void delete_config_files(void) {
   remove(CONFIG_FILE_PATH);
   remove(CONFIG_BACKUP_PATH);
-  // remove(CONFIG_FILE_CHECKSUM_PATH);
-  // remove(CONFIG_BACKUP_CHECKSUM_PATH);
   osi_property_set("persist.bluetooth.factoryreset", "false");
 }
-
-/*static std::string hash_file(const char* filename) {
-  if (!use_key_attestation()) {
-    LOG(INFO) << __func__ << ": Disabled for multi-user";
-    return DISABLED;
-  }
-  FILE* fp = fopen(filename, "rb");
-  if (!fp) {
-    LOG(ERROR) << __func__ << ": unable to open config file: '" << filename
-               << "': " << strerror(errno);
-    return "";
-  }
-  uint8_t hash[SHA256_DIGEST_LENGTH];
-  SHA256_CTX sha256;
-  SHA256_Init(&sha256);
-  std::array<std::byte, kBufferSize> buffer;
-  int bytes_read = 0;
-  while ((bytes_read = fread(buffer.data(), 1, buffer.size(), fp))) {
-    SHA256_Update(&sha256, buffer.data(), bytes_read);
-  }
-  SHA256_Final(hash, &sha256);
-  std::stringstream ss;
-  for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
-    ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
-  }
-  fclose(fp);
-  return ss.str();
-}
-
-static std::string read_checksum_file(const char* checksum_filename) {
-  if (!use_key_attestation()) {
-    LOG(INFO) << __func__ << ": Disabled for multi-user";
-    return DISABLED;
-  }
-  std::string encrypted_hash = checksum_read(checksum_filename);
-  if (encrypted_hash.empty()) {
-    LOG(INFO) << __func__ << ": read empty hash.";
-    return "";
-  }
-  return btif_keystore.Decrypt(encrypted_hash);
-}
-
-static void write_checksum_file(const char* checksum_filename,
-                                const std::string& hash) {
-  if (!use_key_attestation()) {
-    LOG(INFO) << __func__
-              << ": Disabled for multi-user, since config changed removing "
-                 "checksums.";
-    remove(CONFIG_FILE_CHECKSUM_PATH);
-    remove(CONFIG_BACKUP_CHECKSUM_PATH);
-    return;
-  }
-  std::string encrypted_checksum = btif_keystore.Encrypt(hash, 0);
-  CHECK(!encrypted_checksum.empty())
-      << __func__ << ": Failed encrypting checksum";
-  CHECK(checksum_save(encrypted_checksum, checksum_filename))
-      << __func__ << ": Failed to save checksum!";
-}*/
diff --git a/btif/src/btif_config_cache.cc b/btif/src/btif_config_cache.cc
new file mode 100644
index 0000000..dc4ae4d
--- /dev/null
+++ b/btif/src/btif_config_cache.cc
@@ -0,0 +1,306 @@
+/*
+ *  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.
+ */
+
+#include <limits>
+
+#include "btif_config_cache.h"
+
+namespace {
+
+const std::unordered_set<std::string> kLinkKeyTypes = {
+    "LinkKey",      "LE_KEY_PENC", "LE_KEY_PID",
+    "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"};
+
+const std::unordered_set<std::string> kLocalSectionNames = {"Info", "Metrics",
+                                                            "Adapter"};
+
+bool is_link_key(const std::string& key) {
+  return kLinkKeyTypes.find(key) != kLinkKeyTypes.end();
+}
+
+bool has_link_key_in_section(const section_t& section) {
+  for (const auto& entry : section.entries) {
+    if (is_link_key(entry.key)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool is_local_section_info(const std::string& section) {
+  return kLocalSectionNames.find(section) != kLocalSectionNames.end();
+}
+
+// trim new line in place, return true if newline was found
+bool trim_new_line(std::string& value) {
+  size_t newline_position = value.find_first_of('\n');
+  if (newline_position != std::string::npos) {
+    value.erase(newline_position);
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+BtifConfigCache::BtifConfigCache(size_t capacity)
+    : unpaired_devices_cache_(capacity, "bt_config_cache") {
+  LOG(INFO) << __func__ << ", capacity: " << capacity;
+}
+
+BtifConfigCache::~BtifConfigCache() { Clear(); }
+
+void BtifConfigCache::Clear() {
+  unpaired_devices_cache_.Clear();
+  paired_devices_list_.sections.clear();
+}
+
+void BtifConfigCache::Init(std::unique_ptr<config_t> source) {
+  // get the config persistent data from btif_config file
+  paired_devices_list_ = std::move(*source);
+  source.reset();
+}
+
+bool BtifConfigCache::HasPersistentSection(const std::string& section_name) {
+  return paired_devices_list_.Find(section_name) !=
+         paired_devices_list_.sections.end();
+}
+
+bool BtifConfigCache::HasUnpairedSection(const std::string& section_name) {
+  return unpaired_devices_cache_.HasKey(section_name);
+}
+
+bool BtifConfigCache::HasSection(const std::string& section_name) {
+  return HasUnpairedSection(section_name) || HasPersistentSection(section_name);
+}
+
+bool BtifConfigCache::HasKey(const std::string& section_name,
+                             const std::string& key) {
+  auto section_iter = paired_devices_list_.Find(section_name);
+  if (section_iter != paired_devices_list_.sections.end()) {
+    return section_iter->Has(key);
+  }
+  section_t* section = unpaired_devices_cache_.Find(section_name);
+  if (section == nullptr) {
+    return false;
+  }
+  return section->Has(key);
+}
+
+// remove sections with the restricted key
+void BtifConfigCache::RemovePersistentSectionsWithKey(const std::string& key) {
+  for (auto it = paired_devices_list_.sections.begin();
+       it != paired_devices_list_.sections.end();) {
+    if (it->Has(key)) {
+      it = paired_devices_list_.sections.erase(it);
+      continue;
+    }
+    it++;
+  }
+}
+
+/* remove a key from section, section itself is removed when empty */
+bool BtifConfigCache::RemoveKey(const std::string& section_name,
+                                const std::string& key) {
+  section_t* section = unpaired_devices_cache_.Find(section_name);
+  if (section != nullptr) {
+    auto entry_iter = section->Find(key);
+    if (entry_iter == section->entries.end()) {
+      return false;
+    }
+    section->entries.erase(entry_iter);
+    if (section->entries.empty()) {
+      unpaired_devices_cache_.Remove(section_name);
+    }
+    return true;
+  } else {
+    auto section_iter = paired_devices_list_.Find(section_name);
+    if (section_iter == paired_devices_list_.sections.end()) {
+      return false;
+    }
+    auto entry_iter = section_iter->Find(key);
+    if (entry_iter == section_iter->entries.end()) {
+      return false;
+    }
+    section_iter->entries.erase(entry_iter);
+    if (section_iter->entries.empty()) {
+      paired_devices_list_.sections.erase(section_iter);
+    } else if (!has_link_key_in_section(*section_iter)) {
+      // if no link key in section after removal, move it to unpaired section
+      auto moved_section = std::move(*section_iter);
+      paired_devices_list_.sections.erase(section_iter);
+      unpaired_devices_cache_.Put(section_name, std::move(moved_section));
+    }
+    return true;
+  }
+}
+
+std::vector<std::string> BtifConfigCache::GetPersistentSectionNames() {
+  std::vector<std::string> result;
+  result.reserve(paired_devices_list_.sections.size());
+  for (const auto& section : paired_devices_list_.sections) {
+    result.emplace_back(section.name);
+  }
+  return result;
+}
+
+/* clone persistent sections (Local Adapter sections, remote paired devices
+ * section,..) */
+config_t BtifConfigCache::PersistentSectionCopy() {
+  return paired_devices_list_;
+}
+
+void BtifConfigCache::SetString(std::string section_name, std::string key,
+                                std::string value) {
+  if (trim_new_line(section_name) || trim_new_line(key) ||
+      trim_new_line(value)) {
+    android_errorWriteLog(0x534e4554, "70808273");
+  }
+  if (section_name.empty()) {
+    LOG(FATAL) << "Empty section not allowed";
+    return;
+  }
+  if (key.empty()) {
+    LOG(FATAL) << "Empty key not allowed";
+    return;
+  }
+  if (!paired_devices_list_.Has(section_name)) {
+    // section is not in paired_device_list, handle it in unpaired devices cache
+    section_t section = {};
+    bool in_unpaired_cache = true;
+    if (!unpaired_devices_cache_.Get(section_name, &section)) {
+      // it's a new unpaired section, add it to unpaired devices cache
+      section.name = section_name;
+      in_unpaired_cache = false;
+    }
+    // set key to value and replace existing key if already exist
+    section.Set(key, value);
+
+    if (is_local_section_info(section_name) ||
+        (is_link_key(key) && RawAddress::IsValidAddress(section_name))) {
+      // remove this section that has the LinkKey from unpaired devices cache.
+      if (in_unpaired_cache) {
+        unpaired_devices_cache_.Remove(section_name);
+      }
+      // when a unpaired section got the LinkKey, move this section to the
+      // paired devices list
+      paired_devices_list_.sections.emplace_back(std::move(section));
+    } else {
+      // update to the unpaired devices cache
+      unpaired_devices_cache_.Put(section_name, section);
+    }
+  } else {
+    // already have section in paired device list, add key-value entry.
+    auto section_found = paired_devices_list_.Find(section_name);
+    if (section_found == paired_devices_list_.sections.end()) {
+      LOG(WARNING) << __func__ << " , section_found not found!";
+      return;
+    }
+    section_found->Set(key, value);
+  }
+}
+
+std::optional<std::string> BtifConfigCache::GetString(
+    const std::string& section_name, const std::string& key) {
+  // Check paired sections first
+  auto section_iter = paired_devices_list_.Find(section_name);
+  if (section_iter != paired_devices_list_.sections.end()) {
+    auto entry_iter = section_iter->Find(key);
+    if (entry_iter == section_iter->entries.end()) {
+      return std::nullopt;
+    }
+    return entry_iter->value;
+  }
+  // Check unpaired sections later
+  section_t section = {};
+  if (!unpaired_devices_cache_.Get(section_name, &section)) {
+    return std::nullopt;
+  }
+  auto entry_iter = section.Find(key);
+  if (entry_iter == section.entries.end()) {
+    return std::nullopt;
+  }
+  return entry_iter->value;
+}
+
+void BtifConfigCache::SetInt(std::string section_name, std::string key,
+                             int value) {
+  SetString(std::move(section_name), std::move(key), std::to_string(value));
+}
+
+std::optional<int> BtifConfigCache::GetInt(const std::string& section_name,
+                                           const std::string& key) {
+  auto value = GetString(section_name, key);
+  if (!value) {
+    return std::nullopt;
+  }
+  char* endptr;
+  long ret_long = strtol(value->c_str(), &endptr, 0);
+  if (*endptr != '\0') {
+    LOG(WARNING) << "Failed to parse value to long for section " << section_name
+                 << ", key " << key;
+    return std::nullopt;
+  }
+  if (ret_long >= std::numeric_limits<int>::max()) {
+    LOG(WARNING) << "Integer overflow when parsing value to int for section "
+                 << section_name << ", key " << key;
+    return std::nullopt;
+  }
+  return static_cast<int>(ret_long);
+}
+
+void BtifConfigCache::SetUint64(std::string section_name, std::string key,
+                                uint64_t value) {
+  SetString(std::move(section_name), std::move(key), std::to_string(value));
+}
+
+std::optional<uint64_t> BtifConfigCache::GetUint64(
+    const std::string& section_name, const std::string& key) {
+  auto value = GetString(section_name, key);
+  if (!value) {
+    return std::nullopt;
+  }
+  char* endptr;
+  uint64_t ret = strtoull(value->c_str(), &endptr, 0);
+  if (*endptr != '\0') {
+    LOG(WARNING) << "Failed to parse value to uint64 for section "
+                 << section_name << ", key " << key;
+    return std::nullopt;
+  }
+  return ret;
+}
+
+void BtifConfigCache::SetBool(std::string section_name, std::string key,
+                              bool value) {
+  SetString(std::move(section_name), std::move(key), value ? "true" : "false");
+}
+
+std::optional<bool> BtifConfigCache::GetBool(const std::string& section_name,
+                                             const std::string& key) {
+  auto value = GetString(section_name, key);
+  if (!value) {
+    return std::nullopt;
+  }
+  if (*value == "true") {
+    return true;
+  }
+  if (*value == "false") {
+    return false;
+  }
+  LOG(WARNING) << "Failed to parse value to boolean for section "
+               << section_name << ", key " << key;
+  return std::nullopt;
+}
diff --git a/btif/src/btif_config_transcode.cc b/btif/src/btif_config_transcode.cc
index 75ebd77..090b4e1 100644
--- a/btif/src/btif_config_transcode.cc
+++ b/btif/src/btif_config_transcode.cc
@@ -29,15 +29,14 @@
   XMLDocument document;
   int error = document.LoadFile(xml_filename);
   if (error != XML_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s unable to load XML file '%s': %d", __func__,
-              xml_filename, error);
+    LOG_ERROR("%s unable to load XML file '%s': %d", __func__, xml_filename,
+              error);
     return NULL;
   }
 
   XMLElement* rootElement = document.RootElement();
   if (!rootElement) {
-    LOG_ERROR(LOG_TAG,
-              "%s unable to find root element; assuming corrupted config file.",
+    LOG_ERROR("%s unable to find root element; assuming corrupted config file.",
               __func__);
     return NULL;
   }
diff --git a/btif/src/btif_core.cc b/btif/src/btif_core.cc
index aa77979..ac12b1e 100644
--- a/btif/src/btif_core.cc
+++ b/btif/src/btif_core.cc
@@ -322,12 +322,12 @@
  *
  ******************************************************************************/
 bt_status_t btif_init_bluetooth() {
-  LOG_INFO(LOG_TAG, "%s entered", __func__);
+  LOG_INFO("%s entered", __func__);
   exit_manager = new base::AtExitManager();
   bte_main_boot_entry();
   jni_thread.StartUp();
   jni_thread.DoInThread(FROM_HERE, base::Bind(btif_jni_associate));
-  LOG_INFO(LOG_TAG, "%s finished", __func__);
+  LOG_INFO("%s finished", __func__);
   return BT_STATUS_SUCCESS;
 }
 
@@ -343,7 +343,7 @@
  ******************************************************************************/
 
 void btif_enable_bluetooth_evt(tBTA_STATUS status) {
-  LOG_INFO(LOG_TAG, "%s entered: status %d", __func__, status);
+  LOG_INFO("%s entered: status %d", __func__, status);
 
   /* Fetch the local BD ADDR */
   RawAddress local_bd_addr = *controller_get_interface()->get_address();
@@ -399,7 +399,7 @@
     future_ready(stack_manager_get_hack_future(), FUTURE_FAIL);
   }
 
-  LOG_INFO(LOG_TAG, "%s finished", __func__);
+  LOG_INFO("%s finished", __func__);
 }
 
 /*******************************************************************************
@@ -414,7 +414,7 @@
  *
  ******************************************************************************/
 bt_status_t btif_disable_bluetooth() {
-  LOG_INFO(LOG_TAG, "%s entered", __func__);
+  LOG_INFO("%s entered", __func__);
 
   do_in_main_thread(FROM_HERE, base::Bind(&btm_ble_multi_adv_cleanup));
   // TODO(jpawlowski): this should do whole BTA_VendorCleanup(), but it would
@@ -426,7 +426,7 @@
   btif_pan_cleanup();
   BTA_DisableBluetooth();
 
-  LOG_INFO(LOG_TAG, "%s finished", __func__);
+  LOG_INFO("%s finished", __func__);
 
   return BT_STATUS_SUCCESS;
 }
@@ -444,14 +444,14 @@
  ******************************************************************************/
 
 void btif_disable_bluetooth_evt() {
-  LOG_INFO(LOG_TAG, "%s entered", __func__);
+  LOG_INFO("%s entered", __func__);
 
   bte_main_disable();
 
   /* callback to HAL */
   future_ready(stack_manager_get_hack_future(), FUTURE_SUCCESS);
 
-  LOG_INFO(LOG_TAG, "%s finished", __func__);
+  LOG_INFO("%s finished", __func__);
 }
 
 /*******************************************************************************
@@ -465,7 +465,7 @@
  ******************************************************************************/
 
 bt_status_t btif_cleanup_bluetooth() {
-  LOG_INFO(LOG_TAG, "%s entered", __func__);
+  LOG_INFO("%s entered", __func__);
   do_in_main_thread(FROM_HERE, base::Bind(&BTA_VendorCleanup));
   btif_dm_cleanup();
   jni_thread.DoInThread(FROM_HERE, base::BindOnce(btif_jni_disassociate));
@@ -475,7 +475,7 @@
   delete exit_manager;
   exit_manager = nullptr;
   btif_dut_mode = 0;
-  LOG_INFO(LOG_TAG, "%s finished", __func__);
+  LOG_INFO("%s finished", __func__);
   return BT_STATUS_SUCCESS;
 }
 
diff --git a/btif/src/btif_dm.cc b/btif/src/btif_dm.cc
index 5543988..3439fb6 100644
--- a/btif/src/btif_dm.cc
+++ b/btif/src/btif_dm.cc
@@ -60,10 +60,13 @@
 #include "btif_storage.h"
 #include "btif_util.h"
 #include "btu.h"
+#include "common/metric_id_allocator.h"
 #include "common/metrics.h"
 #include "device/include/controller.h"
 #include "device/include/interop.h"
 #include "internal_include/stack_config.h"
+#include "main/shim/btif_dm.h"
+#include "main/shim/shim.h"
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
@@ -72,6 +75,7 @@
 #include "stack_config.h"
 
 using bluetooth::Uuid;
+using bluetooth::common::MetricIdAllocator;
 /******************************************************************************
  *  Constants & Macros
  *****************************************************************************/
@@ -288,7 +292,54 @@
     osi_free_and_reset((void**)&dm_sec->ble_key.p_key_value);
 }
 
-void btif_dm_init(uid_set_t* set) { uid_set = set; }
+static void btif_dm_send_bond_state_changed(RawAddress address, bt_bond_state_t bond_state) {
+  do_in_jni_thread(FROM_HERE, base::BindOnce([](RawAddress address, bt_bond_state_t bond_state) {
+    btif_stats_add_bond_event(address, BTIF_DM_FUNC_BOND_STATE_CHANGED, bond_state);
+    if (bond_state == BT_BOND_STATE_NONE) {
+      MetricIdAllocator::GetInstance().ForgetDevice(address);
+    } else if (bond_state == BT_BOND_STATE_BONDED) {
+      MetricIdAllocator::GetInstance().AllocateId(address);
+      if (!MetricIdAllocator::GetInstance().SaveDevice(address)) {
+        LOG(FATAL) << __func__ << ": Fail to save metric id for device "
+                   << address;
+      }
+    }
+    HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, BT_STATUS_SUCCESS, &address, bond_state);
+  }, address, bond_state));
+}
+
+void btif_dm_init(uid_set_t* set) {
+  uid_set = set;
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    bluetooth::shim::BTIF_DM_SetUiCallback([](RawAddress address, bt_bdname_t bd_name, uint32_t cod, bt_ssp_variant_t pairing_variant, uint32_t pass_key) {
+      do_in_jni_thread(FROM_HERE, base::BindOnce([](RawAddress address, bt_bdname_t bd_name, uint32_t cod, bt_ssp_variant_t pairing_variant, uint32_t pass_key) {
+        LOG(ERROR) << __func__ << ": UI Callback fired!";
+
+        //TODO: java BondStateMachine requires change into bonding state. If we ever send this event separately, consider removing this line
+        HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, BT_STATUS_SUCCESS, &address, BT_BOND_STATE_BONDING);
+
+        if (pairing_variant == BT_SSP_VARIANT_PASSKEY_ENTRY) {
+          // For passkey entry we must actually use pin request, due to BluetoothPairingController (in Settings)
+          HAL_CBACK(bt_hal_cbacks, pin_request_cb, &address, &bd_name, cod, false);
+          return;
+        }
+
+        HAL_CBACK(bt_hal_cbacks, ssp_request_cb, &address, &bd_name, cod, pairing_variant, pass_key);
+      }, address, bd_name, cod, pairing_variant, pass_key));
+    });
+
+    bluetooth::shim::BTIF_RegisterBondStateChangeListener(
+        [](RawAddress address) {
+          btif_dm_send_bond_state_changed(address, BT_BOND_STATE_BONDING);
+        },
+        [](RawAddress address) {
+          btif_dm_send_bond_state_changed(address, BT_BOND_STATE_BONDED);
+        },
+        [](RawAddress address) {
+          btif_dm_send_bond_state_changed(address, BT_BOND_STATE_NONE);
+        });
+  }
+}
 
 void btif_dm_cleanup(void) {
   if (uid_set) {
@@ -416,7 +467,7 @@
                              sizeof(uint32_t), &remote_cod);
   if (btif_storage_get_remote_device_property(
           (RawAddress*)remote_bdaddr, &prop_name) == BT_STATUS_SUCCESS) {
-    LOG_INFO(LOG_TAG, "%s remote_cod = 0x%08x", __func__, remote_cod);
+    LOG_INFO("%s remote_cod = 0x%08x", __func__, remote_cod);
     return remote_cod & COD_MASK;
   }
 
@@ -505,6 +556,15 @@
   BTIF_TRACE_DEBUG("%s: state=%d, prev_state=%d, sdp_attempts = %d", __func__,
                    state, pairing_cb.state, pairing_cb.sdp_attempts);
 
+  if (state == BT_BOND_STATE_NONE) {
+    MetricIdAllocator::GetInstance().ForgetDevice(bd_addr);
+  } else if (state == BT_BOND_STATE_BONDED) {
+    MetricIdAllocator::GetInstance().AllocateId(bd_addr);
+    if (!MetricIdAllocator::GetInstance().SaveDevice(bd_addr)) {
+      LOG(FATAL) << __func__ << ": Fail to save metric id for device "
+                 << bd_addr;
+    }
+  }
   auto tmp = bd_addr;
   HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, status, &tmp, state);
 
@@ -536,8 +596,8 @@
 
   btm_status = BTM_ReadRemoteVersion(*p_bd, &lmp_ver, &mfct_set, &lmp_subver);
 
-  LOG_DEBUG(LOG_TAG, "remote version info [%s]: %x, %x, %x",
-            p_bd->ToString().c_str(), lmp_ver, mfct_set, lmp_subver);
+  LOG_DEBUG("remote version info [%s]: %x, %x, %x", p_bd->ToString().c_str(),
+            lmp_ver, mfct_set, lmp_subver);
 
   if (btm_status == BTM_SUCCESS) {
     // Always update cache to ensure we have availability whenever BTM API is
@@ -663,7 +723,7 @@
   bool is_hid = check_cod(&bd_addr, COD_HID_POINTING);
   bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING);
 
-  int device_type;
+  int device_type = 0;
   int addr_type;
   std::string addrstr = bd_addr.ToString();
   const char* bdstr = addrstr.c_str();
@@ -697,7 +757,7 @@
     if (status != BT_STATUS_SUCCESS)
       bond_state_changed(status, bd_addr, BT_BOND_STATE_NONE);
   } else {
-    BTA_DmBondByTransport(bd_addr, transport);
+    BTA_DmBond(bd_addr, addr_type, transport, device_type);
   }
   /*  Track  originator of bond creation  */
   pairing_cb.is_local_initiated = true;
@@ -987,7 +1047,7 @@
   cod = devclass2uint(p_ssp_cfm_req->dev_class);
 
   if (cod == 0) {
-    LOG_DEBUG(LOG_TAG, "%s cod is 0, set as unclassified", __func__);
+    LOG_DEBUG("%s cod is 0, set as unclassified", __func__);
     cod = COD_UNCLASSIFIED;
   }
 
@@ -1021,7 +1081,7 @@
   cod = devclass2uint(p_ssp_key_notif->dev_class);
 
   if (cod == 0) {
-    LOG_DEBUG(LOG_TAG, "%s cod is 0, set as unclassified", __func__);
+    LOG_DEBUG("%s cod is 0, set as unclassified", __func__);
     cod = COD_UNCLASSIFIED;
   }
 
@@ -1100,13 +1160,13 @@
     bd_addr = p_auth_cmpl->bd_addr;
 
     if (check_sdp_bl(&bd_addr) && check_cod_hid(&bd_addr)) {
-      LOG_WARN(LOG_TAG, "%s:skip SDP", __func__);
+      LOG_WARN("%s:skip SDP", __func__);
       skip_sdp = true;
     }
     if (!pairing_cb.is_local_initiated && skip_sdp) {
       bond_state_changed(status, bd_addr, state);
 
-      LOG_WARN(LOG_TAG, "%s: Incoming HID Connection", __func__);
+      LOG_WARN("%s: Incoming HID Connection", __func__);
       bt_property_t prop;
       Uuid uuid = Uuid::From16Bit(UUID_SERVCLASS_HUMAN_INTERFACE);
 
@@ -1139,8 +1199,7 @@
         if (is_crosskey) {
           // If bonding occurred due to cross-key pairing, send bonding callback
           // for static address now
-          LOG_INFO(LOG_TAG,
-                   "%s: send bonding state update for static address %s",
+          LOG_INFO("%s: send bonding state update for static address %s",
                    __func__, bd_addr.ToString().c_str());
           bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING);
         }
@@ -1152,6 +1211,7 @@
     // Do not call bond_state_changed_cb yet. Wait until remote service
     // discovery is complete
   } else {
+    bool is_bonded_device_removed = false;
     // Map the HCI fail reason  to  bt status
     switch (p_auth_cmpl->fail_reason) {
       case HCI_ERR_PAGE_TIMEOUT:
@@ -1170,14 +1230,16 @@
         break;
 
       case HCI_ERR_PAIRING_NOT_ALLOWED:
-        btif_storage_remove_bonded_device(&bd_addr);
+        is_bonded_device_removed =
+            (btif_storage_remove_bonded_device(&bd_addr) == BT_STATUS_SUCCESS);
         status = BT_STATUS_AUTH_REJECTED;
         break;
 
       /* map the auth failure codes, so we can retry pairing if necessary */
       case HCI_ERR_AUTH_FAILURE:
       case HCI_ERR_KEY_MISSING:
-        btif_storage_remove_bonded_device(&bd_addr);
+        is_bonded_device_removed =
+            (btif_storage_remove_bonded_device(&bd_addr) == BT_STATUS_SUCCESS);
         [[fallthrough]];
       case HCI_ERR_HOST_REJECT_SECURITY:
       case HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE:
@@ -1208,9 +1270,14 @@
       /* Remove Device as bonded in nvram as authentication failed */
       BTIF_TRACE_DEBUG("%s(): removing hid pointing device from nvram",
                        __func__);
-      btif_storage_remove_bonded_device(&bd_addr);
+      is_bonded_device_removed =
+          (btif_storage_remove_bonded_device(&bd_addr) == BT_STATUS_SUCCESS);
     }
-    bond_state_changed(status, bd_addr, state);
+    // Report bond state change to java only if we are bonding to a device or
+    // a device is removed from the pairing list.
+    if (pairing_cb.state == BT_BOND_STATE_BONDING || is_bonded_device_removed) {
+      bond_state_changed(status, bd_addr, state);
+    }
   }
 }
 
@@ -1426,7 +1493,7 @@
         prop.len = p_data->disc_res.num_uuids * Uuid::kNumBytes128;
         for (i = 0; i < p_data->disc_res.num_uuids; i++) {
           std::string temp = ((p_data->disc_res.p_uuid_list + i))->ToString();
-          LOG_INFO(LOG_TAG, "%s index:%d uuid:%s", __func__, i, temp.c_str());
+          LOG_INFO("%s index:%d uuid:%s", __func__, i, temp.c_str());
         }
       }
 
@@ -1436,7 +1503,7 @@
       if (pairing_cb.state == BT_BOND_STATE_BONDED && pairing_cb.sdp_attempts &&
           (p_data->disc_res.bd_addr == pairing_cb.bd_addr ||
            p_data->disc_res.bd_addr == pairing_cb.static_bdaddr)) {
-        LOG_INFO(LOG_TAG, "%s: SDP search done for %s", __func__,
+        LOG_INFO("%s: SDP search done for %s", __func__,
                  bd_addr.ToString().c_str());
         pairing_cb.sdp_attempts = 0;
 
@@ -1448,8 +1515,7 @@
         // or no UUID is discovered
         if (p_data->disc_res.result != BTA_SUCCESS ||
             p_data->disc_res.num_uuids == 0) {
-          LOG_INFO(LOG_TAG,
-                   "%s: SDP failed, send empty UUID to unblock bonding %s",
+          LOG_INFO("%s: SDP failed, send empty UUID to unblock bonding %s",
                    __func__, bd_addr.ToString().c_str());
           bt_property_t prop;
           Uuid uuid = {};
@@ -2374,6 +2440,21 @@
 bt_status_t btif_dm_pin_reply(const RawAddress* bd_addr, uint8_t accept,
                               uint8_t pin_len, bt_pin_code_t* pin_code) {
   BTIF_TRACE_EVENT("%s: accept=%d", __func__, accept);
+
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    if (pin_code == nullptr) {
+      LOG_ERROR("Pin code must be not null with GD shim enabled");
+      return BT_STATUS_FAIL;
+    }
+
+    uint8_t tmp_dev_type = 0;
+    uint8_t tmp_addr_type = 0;
+    BTM_ReadDevInfo(*bd_addr, &tmp_dev_type, &tmp_addr_type);
+
+    do_in_main_thread(FROM_HERE, base::Bind(&bluetooth::shim::BTIF_DM_pin_reply, *bd_addr, tmp_addr_type, accept, pin_len, *pin_code));
+    return BT_STATUS_SUCCESS;
+  }
+
   if (pin_code == NULL || pin_len > PIN_CODE_LEN) return BT_STATUS_FAIL;
   if (pairing_cb.is_le_only) {
     int i;
@@ -2405,6 +2486,16 @@
 bt_status_t btif_dm_ssp_reply(const RawAddress* bd_addr,
                               bt_ssp_variant_t variant, uint8_t accept,
                               UNUSED_ATTR uint32_t passkey) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    uint8_t tmp_dev_type = 0;
+    uint8_t tmp_addr_type = 0;
+    BTM_ReadDevInfo(*bd_addr, &tmp_dev_type, &tmp_addr_type);
+
+    do_in_main_thread(
+    FROM_HERE,
+      base::Bind(&bluetooth::shim::BTIF_DM_ssp_reply, *bd_addr, tmp_addr_type, variant, accept));
+  }
+
   if (variant == BT_SSP_VARIANT_PASSKEY_ENTRY) {
     /* This is not implemented in the stack.
      * For devices with display, this is not needed
@@ -2909,7 +3000,8 @@
         break;
     }
   }
-  if (state == BT_BOND_STATE_BONDED && bd_addr != pairing_cb.static_bdaddr) {
+  if (state == BT_BOND_STATE_BONDED && !pairing_cb.static_bdaddr.IsEmpty() &&
+      bd_addr != pairing_cb.static_bdaddr) {
     // Report RPA bonding state to Java in crosskey paring
     bond_state_changed(status, bd_addr, BT_BOND_STATE_BONDING);
   }
diff --git a/btif/src/btif_gatt_client.cc b/btif/src/btif_gatt_client.cc
index 14433d3..81598bc 100644
--- a/btif/src/btif_gatt_client.cc
+++ b/btif/src/btif_gatt_client.cc
@@ -74,14 +74,14 @@
     }                                                                          \
   } while (0)
 
-#define CHECK_BTGATT_INIT()                                      \
-  do {                                                           \
-    if (bt_gatt_callbacks == NULL) {                             \
-      LOG_WARN(LOG_TAG, "%s: BTGATT not initialized", __func__); \
-      return BT_STATUS_NOT_READY;                                \
-    } else {                                                     \
-      LOG_VERBOSE(LOG_TAG, "%s", __func__);                      \
-    }                                                            \
+#define CHECK_BTGATT_INIT()                             \
+  do {                                                  \
+    if (bt_gatt_callbacks == NULL) {                    \
+      LOG_WARN("%s: BTGATT not initialized", __func__); \
+      return BT_STATUS_NOT_READY;                       \
+    } else {                                            \
+      LOG_VERBOSE("%s", __func__);                      \
+    }                                                   \
   } while (0)
 
 #define BLE_RESOLVE_ADDR_MSB                                                   \
@@ -96,7 +96,7 @@
 uint8_t rssi_request_client_if;
 
 void btif_gattc_upstreams_evt(uint16_t event, char* p_param) {
-  LOG_VERBOSE(LOG_TAG, "%s: Event %d", __func__, event);
+  LOG_VERBOSE("%s: Event %d", __func__, event);
 
   tBTA_GATTC* p_data = (tBTA_GATTC*)p_param;
   switch (event) {
@@ -159,7 +159,7 @@
     }
 
     case BTA_GATTC_ACL_EVT:
-      LOG_DEBUG(LOG_TAG, "BTA_GATTC_ACL_EVT: status = %d", p_data->status);
+      LOG_DEBUG("BTA_GATTC_ACL_EVT: status = %d", p_data->status);
       /* Ignore for now */
       break;
 
@@ -195,7 +195,7 @@
       break;
 
     default:
-      LOG_ERROR(LOG_TAG, "%s: Unhandled event (%d)!", __func__, event);
+      LOG_ERROR("%s: Unhandled event (%d)!", __func__, event);
       break;
   }
 }
diff --git a/btif/src/btif_gatt_server.cc b/btif/src/btif_gatt_server.cc
index 6dc5eb7..a664d27 100644
--- a/btif/src/btif_gatt_server.cc
+++ b/btif/src/btif_gatt_server.cc
@@ -58,14 +58,14 @@
  *  Constants & Macros
  ******************************************************************************/
 
-#define CHECK_BTGATT_INIT()                                      \
-  do {                                                           \
-    if (bt_gatt_callbacks == NULL) {                             \
-      LOG_WARN(LOG_TAG, "%s: BTGATT not initialized", __func__); \
-      return BT_STATUS_NOT_READY;                                \
-    } else {                                                     \
-      LOG_VERBOSE(LOG_TAG, "%s", __func__);                      \
-    }                                                            \
+#define CHECK_BTGATT_INIT()                             \
+  do {                                                  \
+    if (bt_gatt_callbacks == NULL) {                    \
+      LOG_WARN("%s: BTGATT not initialized", __func__); \
+      return BT_STATUS_NOT_READY;                       \
+    } else {                                            \
+      LOG_VERBOSE("%s", __func__);                      \
+    }                                                   \
   } while (0)
 
 /*******************************************************************************
@@ -124,7 +124,7 @@
 }
 
 static void btapp_gatts_handle_cback(uint16_t event, char* p_param) {
-  LOG_VERBOSE(LOG_TAG, "%s: Event %d", __func__, event);
+  LOG_VERBOSE("%s: Event %d", __func__, event);
 
   tBTA_GATTS* p_data = (tBTA_GATTS*)p_param;
   switch (event) {
@@ -231,7 +231,7 @@
     case BTA_GATTS_OPEN_EVT:
     case BTA_GATTS_CANCEL_OPEN_EVT:
     case BTA_GATTS_CLOSE_EVT:
-      LOG_DEBUG(LOG_TAG, "%s: Empty event (%d)!", __func__, event);
+      LOG_DEBUG("%s: Empty event (%d)!", __func__, event);
       break;
 
     case BTA_GATTS_PHY_UPDATE_EVT:
@@ -248,7 +248,7 @@
       break;
 
     default:
-      LOG_ERROR(LOG_TAG, "%s: Unhandled event (%d)!", __func__, event);
+      LOG_ERROR("%s: Unhandled event (%d)!", __func__, event);
       break;
   }
 
@@ -356,7 +356,7 @@
   // refactored, and one can distinguish stack-internal aps from external apps
   if (service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER) ||
       service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
-    LOG_ERROR(LOG_TAG, "%s: Attept to register restricted service", __func__);
+    LOG_ERROR("%s: Attept to register restricted service", __func__);
     HAL_CBACK(bt_gatt_callbacks, server->service_added_cb, BT_STATUS_FAIL,
               server_if, std::move(service));
     return;
diff --git a/btif/src/btif_gatt_test.cc b/btif/src/btif_gatt_test.cc
index 1c0af35..9bb4dd8 100644
--- a/btif/src/btif_gatt_test.cc
+++ b/btif/src/btif_gatt_test.cc
@@ -69,16 +69,15 @@
 static void btif_test_connect_cback(tGATT_IF, const RawAddress&,
                                     uint16_t conn_id, bool connected,
                                     tGATT_DISCONN_REASON, tBT_TRANSPORT) {
-  LOG_DEBUG(LOG_TAG, "%s: conn_id=%d, connected=%d", __func__, conn_id,
-            connected);
+  LOG_DEBUG("%s: conn_id=%d, connected=%d", __func__, conn_id, connected);
   test_cb.conn_id = connected ? conn_id : 0;
 }
 
 static void btif_test_command_complete_cback(uint16_t conn_id, tGATTC_OPTYPE op,
                                              tGATT_STATUS status,
                                              tGATT_CL_COMPLETE* p_data) {
-  LOG_DEBUG(LOG_TAG, "%s: op_code=0x%02x, conn_id=0x%x. status=0x%x", __func__,
-            op, conn_id, status);
+  LOG_DEBUG("%s: op_code=0x%02x, conn_id=0x%x. status=0x%x", __func__, op,
+            conn_id, status);
 
   switch (op) {
     case GATTC_OPTYPE_READ:
@@ -93,7 +92,7 @@
       break;
 
     default:
-      LOG_DEBUG(LOG_TAG, "%s: Unknown op_code (0x%02x)", __func__, op);
+      LOG_DEBUG("%s: Unknown op_code (0x%02x)", __func__, op);
       break;
   }
 }
@@ -101,62 +100,58 @@
 static void btif_test_discovery_result_cback(UNUSED_ATTR uint16_t conn_id,
                                              tGATT_DISC_TYPE disc_type,
                                              tGATT_DISC_RES* p_data) {
-  LOG_DEBUG(LOG_TAG, "------ GATT Discovery result %-22s -------",
-            disc_name[disc_type]);
-  LOG_DEBUG(LOG_TAG, "      Attribute handle: 0x%04x (%d)", p_data->handle,
+  LOG_DEBUG("------ GATT Discovery result %-22s -------", disc_name[disc_type]);
+  LOG_DEBUG("      Attribute handle: 0x%04x (%d)", p_data->handle,
             p_data->handle);
 
   if (disc_type != GATT_DISC_CHAR_DSCPT) {
-    LOG_DEBUG(LOG_TAG, "        Attribute type: %s",
-              p_data->type.ToString().c_str());
+    LOG_DEBUG("        Attribute type: %s", p_data->type.ToString().c_str());
   }
 
   switch (disc_type) {
     case GATT_DISC_SRVC_ALL:
-      LOG_DEBUG(LOG_TAG, "          Handle range: 0x%04x ~ 0x%04x (%d ~ %d)",
+      LOG_DEBUG("          Handle range: 0x%04x ~ 0x%04x (%d ~ %d)",
                 p_data->handle, p_data->value.group_value.e_handle,
                 p_data->handle, p_data->value.group_value.e_handle);
-      LOG_DEBUG(LOG_TAG, "          Service UUID: %s",
+      LOG_DEBUG("          Service UUID: %s",
                 p_data->value.group_value.service_type.ToString().c_str());
       break;
 
     case GATT_DISC_SRVC_BY_UUID:
-      LOG_DEBUG(LOG_TAG, "          Handle range: 0x%04x ~ 0x%04x (%d ~ %d)",
+      LOG_DEBUG("          Handle range: 0x%04x ~ 0x%04x (%d ~ %d)",
                 p_data->handle, p_data->value.handle, p_data->handle,
                 p_data->value.handle);
       break;
 
     case GATT_DISC_INC_SRVC:
-      LOG_DEBUG(LOG_TAG, "          Handle range: 0x%04x ~ 0x%04x (%d ~ %d)",
+      LOG_DEBUG("          Handle range: 0x%04x ~ 0x%04x (%d ~ %d)",
                 p_data->value.incl_service.s_handle,
                 p_data->value.incl_service.e_handle,
                 p_data->value.incl_service.s_handle,
                 p_data->value.incl_service.e_handle);
-      LOG_DEBUG(LOG_TAG, "          Service UUID: %s",
+      LOG_DEBUG("          Service UUID: %s",
                 p_data->value.incl_service.service_type.ToString().c_str());
       break;
 
     case GATT_DISC_CHAR:
-      LOG_DEBUG(LOG_TAG, "            Properties: 0x%02x",
+      LOG_DEBUG("            Properties: 0x%02x",
                 p_data->value.dclr_value.char_prop);
-      LOG_DEBUG(LOG_TAG, "   Characteristic UUID: %s",
+      LOG_DEBUG("   Characteristic UUID: %s",
                 p_data->value.dclr_value.char_uuid.ToString().c_str());
       break;
 
     case GATT_DISC_CHAR_DSCPT:
-      LOG_DEBUG(LOG_TAG, "       Descriptor UUID: %s",
-                p_data->type.ToString().c_str());
+      LOG_DEBUG("       Descriptor UUID: %s", p_data->type.ToString().c_str());
       break;
   }
 
-  LOG_DEBUG(LOG_TAG,
-            "-----------------------------------------------------------");
+  LOG_DEBUG("-----------------------------------------------------------");
 }
 
 static void btif_test_discovery_complete_cback(
     UNUSED_ATTR uint16_t conn_id, UNUSED_ATTR tGATT_DISC_TYPE disc_type,
     tGATT_STATUS status) {
-  LOG_DEBUG(LOG_TAG, "%s: status=%d", __func__, status);
+  LOG_DEBUG("%s: status=%d", __func__, status);
 }
 
 static tGATT_CBACK btif_test_callbacks = {btif_test_connect_cback,
@@ -178,7 +173,7 @@
   switch (command) {
     case 0x01: /* Enable */
     {
-      LOG_DEBUG(LOG_TAG, "%s: ENABLE - enable=%d", __func__, params->u1);
+      LOG_DEBUG("%s: ENABLE - enable=%d", __func__, params->u1);
       if (params->u1) {
         std::array<uint8_t, Uuid::kNumBytes128> tmp;
         tmp.fill(0xAE);
@@ -194,9 +189,8 @@
 
     case 0x02: /* Connect */
     {
-      LOG_DEBUG(LOG_TAG, "%s: CONNECT - device=%s (dev_type=%d, addr_type=%d)",
-                __func__, params->bda1->ToString().c_str(), params->u1,
-                params->u2);
+      LOG_DEBUG("%s: CONNECT - device=%s (dev_type=%d, addr_type=%d)", __func__,
+                params->bda1->ToString().c_str(), params->u1, params->u2);
 
       if (params->u1 == BT_DEVICE_TYPE_BLE)
         BTM_SecAddBleDevice(*params->bda1, NULL, BT_DEVICE_TYPE_BLE,
@@ -204,15 +198,14 @@
 
       if (!GATT_Connect(test_cb.gatt_if, *params->bda1, true, BT_TRANSPORT_LE,
                         false)) {
-        LOG_ERROR(LOG_TAG, "%s: GATT_Connect failed!", __func__);
+        LOG_ERROR("%s: GATT_Connect failed!", __func__);
       }
       break;
     }
 
     case 0x03: /* Disconnect */
     {
-      LOG_DEBUG(LOG_TAG, "%s: DISCONNECT - conn_id=%d", __func__,
-                test_cb.conn_id);
+      LOG_DEBUG("%s: DISCONNECT - conn_id=%d", __func__, test_cb.conn_id);
       GATT_Disconnect(test_cb.conn_id);
       break;
     }
@@ -220,13 +213,11 @@
     case 0x04: /* Discover */
     {
       if (params->u1 >= GATT_DISC_MAX) {
-        LOG_ERROR(LOG_TAG, "%s: DISCOVER - Invalid type (%d)!", __func__,
-                  params->u1);
+        LOG_ERROR("%s: DISCOVER - Invalid type (%d)!", __func__, params->u1);
         return (bt_status_t)0;
       }
 
-      LOG_DEBUG(LOG_TAG,
-                "%s: DISCOVER (%s), conn_id=%d, uuid=%s, handles=0x%04x-0x%04x",
+      LOG_DEBUG("%s: DISCOVER (%s), conn_id=%d, uuid=%s, handles=0x%04x-0x%04x",
                 __func__, disc_name[params->u1], test_cb.conn_id,
                 params->uuid1->ToString().c_str(), params->u2, params->u3);
       GATTC_Discover(test_cb.conn_id, params->u1, params->u2, params->u3,
@@ -235,8 +226,7 @@
     }
 
     case 0xF0: /* Pairing configuration */
-      LOG_DEBUG(LOG_TAG,
-                "%s: Setting pairing config auth=%d, iocaps=%d, keys=%d/%d/%d",
+      LOG_DEBUG("%s: Setting pairing config auth=%d, iocaps=%d, keys=%d/%d/%d",
                 __func__, params->u1, params->u2, params->u3, params->u4,
                 params->u5);
 
@@ -248,7 +238,7 @@
       break;
 
     default:
-      LOG_ERROR(LOG_TAG, "%s: UNKNOWN TEST COMMAND 0x%02x", __func__, command);
+      LOG_ERROR("%s: UNKNOWN TEST COMMAND 0x%02x", __func__, command);
       break;
   }
   return (bt_status_t)0;
diff --git a/btif/src/btif_gatt_util.cc b/btif/src/btif_gatt_util.cc
index 16f2275..2f2df59 100644
--- a/btif/src/btif_gatt_util.cc
+++ b/btif/src/btif_gatt_util.cc
@@ -76,7 +76,7 @@
                                     tGATT_TRANSPORT transport_link) {
   tBTM_LE_PENC_KEYS key;
   if ((btif_storage_get_ble_bonding_key(
-           &bd_addr, BTIF_DM_LE_KEY_PENC, (uint8_t*)&key,
+           bd_addr, BTIF_DM_LE_KEY_PENC, (uint8_t*)&key,
            sizeof(tBTM_LE_PENC_KEYS)) == BT_STATUS_SUCCESS) &&
       !btif_gatt_is_link_encrypted(bd_addr)) {
     BTIF_TRACE_DEBUG("%s: transport = %d", __func__, transport_link);
diff --git a/btif/src/btif_hd.cc b/btif/src/btif_hd.cc
index d580a19..7942693 100644
--- a/btif/src/btif_hd.cc
+++ b/btif/src/btif_hd.cc
@@ -211,7 +211,7 @@
         BTA_HdDisconnect();
         break;
       }
-      btif_storage_set_hidd((RawAddress*)&p_data->conn.bda);
+      btif_storage_set_hidd(p_data->conn.bda);
 
       HAL_CBACK(bt_hd_callbacks, connection_state_cb,
                 (RawAddress*)&p_data->conn.bda, BTHD_CONN_STATE_CONNECTED);
diff --git a/btif/src/btif_hf.cc b/btif/src/btif_hf.cc
index 04ad011..ec4c13f 100644
--- a/btif/src/btif_hf.cc
+++ b/btif/src/btif_hf.cc
@@ -319,12 +319,48 @@
     case BTA_AG_OPEN_EVT:
       // Check if an outoging connection is pending
       if (btif_hf_cb[idx].is_initiator) {
+        if ((p_data->open.status != BTA_AG_SUCCESS) &&
+            btif_hf_cb[idx].state != BTHF_CONNECTION_STATE_CONNECTING) {
+          if (p_data->open.bd_addr == btif_hf_cb[idx].connected_bda) {
+            LOG(WARNING) << __func__ << ": btif_hf_cb state["
+                         << p_data->open.status
+                         << "] is not expected, possible connection collision, "
+                            "ignoring AG open "
+                            "failure event for the same device "
+                         << p_data->open.bd_addr;
+          } else {
+            LOG(WARNING) << __func__ << ": btif_hf_cb state["
+                         << p_data->open.status
+                         << "] is not expected, possible connection collision, "
+                            "ignoring AG open failure "
+                            "event for the different devices btif_hf_cb bda: "
+                         << btif_hf_cb[idx].connected_bda
+                         << ", p_data bda: " << p_data->open.bd_addr
+                         << ", report disconnect state for p_data bda.";
+            bt_hf_callbacks->ConnectionStateCallback(
+                BTHF_CONNECTION_STATE_DISCONNECTED, &(p_data->open.bd_addr));
+          }
+          break;
+        }
+
         CHECK_EQ(btif_hf_cb[idx].state, BTHF_CONNECTION_STATE_CONNECTING)
             << "Control block must be in connecting state when initiating";
         CHECK(!btif_hf_cb[idx].connected_bda.IsEmpty())
             << "Remote device address must not be empty when initiating";
-        CHECK_EQ(btif_hf_cb[idx].connected_bda, p_data->open.bd_addr)
-            << "Incoming message's address must match expected one";
+        if (btif_hf_cb[idx].connected_bda != p_data->open.bd_addr) {
+          LOG(WARNING) << __func__
+                       << ": possible connection collision, ignore the "
+                          "outgoing connection for the "
+                          "different devices btif_hf_cb bda: "
+                       << btif_hf_cb[idx].connected_bda
+                       << ", p_data bda: " << p_data->open.bd_addr
+                       << ", report disconnect state for btif_hf_cb bda.";
+          bt_hf_callbacks->ConnectionStateCallback(
+              BTHF_CONNECTION_STATE_DISCONNECTED,
+              &(btif_hf_cb[idx].connected_bda));
+          reset_control_block(&btif_hf_cb[idx]);
+          btif_queue_advance();
+        }
       }
       if (p_data->open.status == BTA_AG_SUCCESS) {
         // In case this is an incoming connection
@@ -1324,14 +1360,18 @@
   BTIF_TRACE_EVENT("%s", __func__);
 
   btif_queue_cleanup(UUID_SERVCLASS_AG_HANDSFREE);
-  if (bt_hf_callbacks) {
+
+  tBTA_SERVICE_MASK mask = btif_get_enabled_services_mask();
 #if (defined(BTIF_HF_SERVICES) && (BTIF_HF_SERVICES & BTA_HFP_SERVICE_MASK))
+  if ((mask & (1 << BTA_HFP_SERVICE_ID)) != 0) {
     btif_disable_service(BTA_HFP_SERVICE_ID);
-#else
-    btif_disable_service(BTA_HSP_SERVICE_ID);
-#endif
-    bt_hf_callbacks = nullptr;
   }
+#else
+  if ((mask & (1 << BTA_HSP_SERVICE_ID)) != 0) {
+    btif_disable_service(BTA_HSP_SERVICE_ID);
+  }
+#endif
+  do_in_jni_thread(FROM_HERE, base::Bind([]() { bt_hf_callbacks = nullptr; }));
 }
 
 bt_status_t HeadsetInterface::SetScoAllowed(bool value) {
diff --git a/btif/src/btif_hf_client.cc b/btif/src/btif_hf_client.cc
old mode 100644
new mode 100755
index e7181e9..63abecd
--- a/btif/src/btif_hf_client.cc
+++ b/btif/src/btif_hf_client.cc
@@ -1044,12 +1044,16 @@
 bt_status_t btif_hf_client_execute_service(bool b_enable) {
   BTIF_TRACE_EVENT("%s: enable: %d", __func__, b_enable);
 
+  tBTA_HF_CLIENT_FEAT features = BTIF_HF_CLIENT_FEATURES;
+  if (osi_property_get_bool("persist.bluetooth.hfpclient.sco_s4_supported", false))
+    features |= BTA_HF_CLIENT_FEAT_S4;
+
   if (b_enable) {
     /* Enable and register with BTA-HFClient */
     BTIF_TRACE_EVENT("%s: support codec negotiation %d ", __func__,
-                     BTIF_HF_CLIENT_FEATURES);
+                     features);
     BTA_HfClientEnable(bta_hf_client_evt, BTIF_HF_CLIENT_SECURITY,
-                       BTIF_HF_CLIENT_FEATURES, BTIF_HF_CLIENT_SERVICE_NAME);
+                       features, BTIF_HF_CLIENT_SERVICE_NAME);
   } else {
     BTA_HfClientDisable();
   }
diff --git a/btif/src/btif_hh.cc b/btif/src/btif_hh.cc
index a1df515..5f1e5a8 100644
--- a/btif/src/btif_hh.cc
+++ b/btif/src/btif_hh.cc
@@ -79,10 +79,6 @@
 #define BTUI_HH_SECURITY (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)
 #endif
 
-#ifndef BTUI_HH_MOUSE_SECURITY
-#define BTUI_HH_MOUSE_SECURITY (BTA_SEC_NONE)
-#endif
-
 /* HH request events */
 typedef enum {
   BTIF_HH_CONNECT_REQ_EVT = 0,
@@ -464,7 +460,7 @@
     p_added_dev = &btif_hh_cb.added_devices[i];
     if (p_added_dev->bd_addr == bd_addr) {
       BTA_HhRemoveDev(p_added_dev->dev_handle);
-      btif_storage_remove_hid_info(&(p_added_dev->bd_addr));
+      btif_storage_remove_hid_info(p_added_dev->bd_addr);
       memset(&(p_added_dev->bd_addr), 0, 6);
       p_added_dev->dev_handle = BTA_HH_INVALID_HANDLE;
       break;
@@ -613,10 +609,9 @@
    request from host, for subsequent user initiated connection. If the remote is
    not in
    pagescan mode, we will do 2 retries to connect before giving up */
-  tBTA_SEC sec_mask = BTUI_HH_SECURITY;
   btif_hh_cb.status = BTIF_HH_DEV_CONNECTING;
   btif_hh_cb.pending_conn_address = *bd_addr;
-  BTA_HhOpen(*bd_addr, BTA_HH_PROTO_RPT_MODE, sec_mask);
+  BTA_HhOpen(*bd_addr, BTA_HH_PROTO_RPT_MODE, BTUI_HH_SECURITY);
 
   // TODO(jpawlowski); make cback accept const and remove tmp!
   auto tmp = *bd_addr;
@@ -965,7 +960,7 @@
       }
       if (p_dev->fd < 0) {
         LOG_ERROR(
-            LOG_TAG,
+
             "BTA_HH_GET_DSCP_EVT: Error, failed to find the uhid driver...");
         return;
       }
@@ -1089,7 +1084,7 @@
       break;
 
     case BTA_HH_API_ERR_EVT:
-      LOG_INFO(LOG_TAG, "BTA_HH API_ERR");
+      LOG_INFO("BTA_HH API_ERR");
       break;
 
     default:
@@ -1555,7 +1550,7 @@
     /* Build a SetReport data buffer */
     // TODO
     hex_bytes_filled = ascii_2_hex(report, len, hexbuf);
-    LOG_INFO(LOG_TAG, "Hex bytes filled, hex value: %d", hex_bytes_filled);
+    LOG_INFO("Hex bytes filled, hex value: %d", hex_bytes_filled);
     if (hex_bytes_filled) {
       BT_HDR* p_buf = create_pbuf(hex_bytes_filled, hexbuf);
       if (p_buf == NULL) {
diff --git a/btif/src/btif_keystore.cc b/btif/src/btif_keystore.cc
index fe9d3dd..86194b7 100644
--- a/btif/src/btif_keystore.cc
+++ b/btif/src/btif_keystore.cc
@@ -1,101 +1,110 @@
-/******************************************************************************
+/*
+ * Copyright 2020 The Android Open Source Project
  *
- *  Copyright 2019 Google, Inc.
+ * 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
  *
- *  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
  *
- *  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.
- *
- ******************************************************************************/
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
-#include "btif_keystore.h"
+/* BluetoothKeystore Interface */
 
-#include <base/files/file_util.h>
+#include <btif_common.h>
+#include <btif_keystore.h>
+
+#include <base/bind.h>
+#include <base/location.h>
 #include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
-#include <base/strings/utf_string_conversions.h>
-#include <sys/stat.h>
+#include <hardware/bluetooth.h>
+#include <map>
 
-using namespace keystore;
-using namespace bluetooth;
-
-constexpr char kKeyStore[] = "AndroidKeystore";
+using base::Bind;
+using base::Unretained;
+using bluetooth::bluetooth_keystore::BluetoothKeystoreCallbacks;
+using bluetooth::bluetooth_keystore::BluetoothKeystoreInterface;
 
 namespace bluetooth {
+namespace bluetooth_keystore {
+class BluetoothKeystoreInterfaceImpl;
+std::unique_ptr<BluetoothKeystoreInterface> bluetoothKeystoreInstance;
 
-BtifKeystore::BtifKeystore(keystore::KeystoreClient* keystore_client)
-    : keystore_client_(keystore_client) {}
+class BluetoothKeystoreInterfaceImpl
+    : public bluetooth::bluetooth_keystore::BluetoothKeystoreInterface,
+      public bluetooth::bluetooth_keystore::BluetoothKeystoreCallbacks {
+  ~BluetoothKeystoreInterfaceImpl() override = default;
 
-std::string BtifKeystore::Encrypt(const std::string& data, int32_t flags) {
-  std::lock_guard<std::mutex> lock(api_mutex_);
-  std::string output;
-  if (data.empty()) {
-    LOG(ERROR) << __func__ << ": empty data";
-    return output;
+  void init(BluetoothKeystoreCallbacks* callbacks) override {
+    VLOG(2) << __func__;
+    this->callbacks = callbacks;
   }
-  if (!keystore_client_->doesKeyExist(kKeyStore)) {
-    auto gen_result = GenerateKey(kKeyStore, 0, false);
-    if (!gen_result.isOk()) {
-      LOG(FATAL) << "EncryptWithAuthentication Failed: generateKey response="
-                 << gen_result;
-      return output;
+
+  void set_encrypt_key_or_remove_key(std::string prefix,
+                                     std::string decryptedString) override {
+    VLOG(2) << __func__ << " prefix: " << prefix;
+
+    if (!callbacks) {
+      LOG(WARNING) << __func__ << " callback isn't ready. prefix: " << prefix;
+      return;
     }
+
+    // Save the value into a map.
+    key_map[prefix] = decryptedString;
+
+    do_in_jni_thread(
+        base::Bind(&bluetooth::bluetooth_keystore::BluetoothKeystoreCallbacks::
+                       set_encrypt_key_or_remove_key,
+                   base::Unretained(callbacks), prefix, decryptedString));
   }
-  if (!keystore_client_->encryptWithAuthentication(kKeyStore, data, flags,
-                                                   &output)) {
-    LOG(FATAL) << "EncryptWithAuthentication failed.";
-    return output;
+
+  std::string get_key(std::string prefix) override {
+    VLOG(2) << __func__ << " prefix: " << prefix;
+
+    if (!callbacks) {
+      LOG(WARNING) << __func__ << " callback isn't ready. prefix: " << prefix;
+      return "";
+    }
+
+    std::string decryptedString;
+    // try to find the key.
+    std::map<std::string, std::string>::iterator iter = key_map.find(prefix);
+    if (iter == key_map.end()) {
+      decryptedString = callbacks->get_key(prefix);
+      // Save the value into a map.
+      key_map[prefix] = decryptedString;
+      VLOG(2) << __func__ << ": get key from bluetoothkeystore.";
+    } else {
+      decryptedString = iter->second;
+    }
+    return decryptedString;
   }
-  return output;
+
+  void clear_map() override {
+    VLOG(2) << __func__;
+
+    std::map<std::string, std::string> empty_map;
+    key_map.swap(empty_map);
+    key_map.clear();
+  }
+
+ private:
+  BluetoothKeystoreCallbacks* callbacks = nullptr;
+  std::map<std::string, std::string> key_map;
+};
+
+BluetoothKeystoreInterface* getBluetoothKeystoreInterface() {
+  if (!bluetoothKeystoreInstance) {
+    bluetoothKeystoreInstance.reset(new BluetoothKeystoreInterfaceImpl());
+  }
+
+  return bluetoothKeystoreInstance.get();
 }
 
-std::string BtifKeystore::Decrypt(const std::string& input) {
-  std::lock_guard<std::mutex> lock(api_mutex_);
-  if (input.empty()) {
-    LOG(ERROR) << __func__ << ": empty input data";
-    return "";
-  }
-  std::string output;
-  if (!keystore_client_->decryptWithAuthentication(kKeyStore, input, &output)) {
-    LOG(FATAL) << "DecryptWithAuthentication failed.\n";
-  }
-  return output;
-}
-
-// Note: auth_bound keys created with this tool will not be usable.
-KeyStoreNativeReturnCode BtifKeystore::GenerateKey(const std::string& name,
-                                                   int32_t flags,
-                                                   bool auth_bound) {
-  AuthorizationSetBuilder params;
-  params.RsaSigningKey(2048, 65537)
-      .Digest(Digest::SHA_2_224)
-      .Digest(Digest::SHA_2_256)
-      .Digest(Digest::SHA_2_384)
-      .Digest(Digest::SHA_2_512)
-      .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
-      .Padding(PaddingMode::RSA_PSS);
-  if (auth_bound) {
-    // Gatekeeper normally generates the secure user id.
-    // Using zero allows the key to be created, but it will not be usuable.
-    params.Authorization(TAG_USER_SECURE_ID, 0);
-  } else {
-    params.Authorization(TAG_NO_AUTH_REQUIRED);
-  }
-  AuthorizationSet hardware_enforced_characteristics;
-  AuthorizationSet software_enforced_characteristics;
-  return keystore_client_->generateKey(name, params, flags,
-                                       &hardware_enforced_characteristics,
-                                       &software_enforced_characteristics);
-}
-
+}  // namespace bluetooth_keystore
 }  // namespace bluetooth
diff --git a/btif/src/btif_pan.cc b/btif/src/btif_pan.cc
index fe10b49..9bde904 100644
--- a/btif/src/btif_pan.cc
+++ b/btif/src/btif_pan.cc
@@ -420,8 +420,7 @@
     char packet[TAP_MAX_PKT_WRITE_LEN + sizeof(tETH_HDR)];
     memcpy(packet, &eth_hdr, sizeof(tETH_HDR));
     if (len > TAP_MAX_PKT_WRITE_LEN) {
-      LOG_ERROR(LOG_TAG, "btpan_tap_send eth packet size:%d is exceeded limit!",
-                len);
+      LOG_ERROR("btpan_tap_send eth packet size:%d is exceeded limit!", len);
       return -1;
     }
     memcpy(packet + sizeof(tETH_HDR), buf, len);
@@ -616,7 +615,7 @@
       bt_status_t status;
       btpan_conn_t* conn = btpan_find_conn_handle(p_data->open.handle);
 
-      LOG_VERBOSE(LOG_TAG, "%s pan connection open status: %d", __func__,
+      LOG_VERBOSE("%s pan connection open status: %d", __func__,
                   p_data->open.status);
       if (p_data->open.status == BTA_PAN_SUCCESS) {
         state = BTPAN_STATE_CONNECTED;
@@ -638,7 +637,7 @@
       break;
     }
     case BTA_PAN_CLOSE_EVT: {
-      LOG_INFO(LOG_TAG, "%s: event = BTA_PAN_CLOSE_EVT handle %d", __func__,
+      LOG_INFO("%s: event = BTA_PAN_CLOSE_EVT handle %d", __func__,
                p_data->close.handle);
       btpan_conn_t* conn = btpan_find_conn_handle(p_data->close.handle);
       btpan_close_conn(conn);
diff --git a/btif/src/btif_profile_queue.cc b/btif/src/btif_profile_queue.cc
index 900a6af..231e2fe 100644
--- a/btif/src/btif_profile_queue.cc
+++ b/btif/src/btif_profile_queue.cc
@@ -99,13 +99,13 @@
   ConnectNode param(bda, uuid, connect_cb);
   for (const auto& node : connect_queue) {
     if (node.uuid() == param.uuid() && node.address() == param.address()) {
-      LOG_ERROR(LOG_TAG, "%s: dropping duplicate connection request: %s",
-                __func__, param.ToString().c_str());
+      LOG_ERROR("%s: dropping duplicate connection request: %s", __func__,
+                param.ToString().c_str());
       return;
     }
   }
 
-  LOG_INFO(LOG_TAG, "%s: adding connection request: %s", __func__,
+  LOG_INFO("%s: adding connection request: %s", __func__,
            param.ToString().c_str());
   connect_queue.push_back(param);
 
@@ -116,7 +116,7 @@
   if (connect_queue.empty()) return;
 
   const ConnectNode& head = connect_queue.front();
-  LOG_INFO(LOG_TAG, "%s: removing connection request: %s", __func__,
+  LOG_INFO("%s: removing connection request: %s", __func__,
            head.ToString().c_str());
   connect_queue.pop_front();
 
@@ -124,13 +124,13 @@
 }
 
 static void queue_int_cleanup(uint16_t uuid) {
-  LOG_INFO(LOG_TAG, "%s: UUID=%04X", __func__, uuid);
+  LOG_INFO("%s: UUID=%04X", __func__, uuid);
 
   for (auto it = connect_queue.begin(); it != connect_queue.end();) {
     auto it_prev = it++;
     const ConnectNode& node = *it_prev;
     if (node.uuid() == uuid) {
-      LOG_INFO(LOG_TAG, "%s: removing connection request: %s", __func__,
+      LOG_INFO("%s: removing connection request: %s", __func__,
                node.ToString().c_str());
       connect_queue.erase(it_prev);
     }
@@ -193,12 +193,11 @@
 
   ConnectNode& head = connect_queue.front();
 
-  LOG_INFO(LOG_TAG, "%s: executing connection request: %s", __func__,
+  LOG_INFO("%s: executing connection request: %s", __func__,
            head.ToString().c_str());
   bt_status_t b_status = head.connect();
   if (b_status != BT_STATUS_SUCCESS) {
-    LOG_INFO(LOG_TAG,
-             "%s: connect %s failed, advance to next scheduled connection.",
+    LOG_INFO("%s: connect %s failed, advance to next scheduled connection.",
              __func__, head.ToString().c_str());
     btif_queue_advance();
   }
@@ -215,7 +214,7 @@
  *
  ******************************************************************************/
 void btif_queue_release() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   if (do_in_jni_thread(FROM_HERE, base::Bind(&queue_int_release)) !=
       BT_STATUS_SUCCESS) {
     LOG(FATAL) << __func__ << ": Failed to schedule on JNI thread";
diff --git a/btif/src/btif_rc.cc b/btif/src/btif_rc.cc
old mode 100644
new mode 100755
index b61bf4f..c99ca57
--- a/btif/src/btif_rc.cc
+++ b/btif/src/btif_rc.cc
@@ -46,8 +46,8 @@
 #include "btif_util.h"
 #include "btu.h"
 #include "device/include/interop.h"
-#include "log/log.h"
 #include "osi/include/list.h"
+#include "osi/include/log.h"
 #include "osi/include/osi.h"
 #include "osi/include/properties.h"
 
@@ -174,6 +174,7 @@
   bool br_connected;  // Browsing channel.
   uint8_t rc_handle;
   tBTA_AV_FEAT rc_features;
+  uint16_t rc_cover_art_psm;  // AVRCP-BIP psm
   btrc_connection_state_t rc_state;
   RawAddress rc_addr;
   uint16_t rc_pending_play;
@@ -294,7 +295,7 @@
                                                  tAVRC_RSP* p_rsp);
 static void cleanup_btrc_folder_items(btrc_folder_items_t* btrc_items,
                                       uint8_t item_count);
-static void handle_get_elem_attr_response(tBTA_AV_META_MSG* pmeta_msg,
+static void handle_get_metadata_attr_response(tBTA_AV_META_MSG* pmeta_msg,
                                           tAVRC_GET_ATTRS_RSP* p_rsp);
 static void handle_set_app_attr_val_response(tBTA_AV_META_MSG* pmeta_msg,
                                              tAVRC_RSP* p_rsp);
@@ -306,9 +307,16 @@
 static bt_status_t register_notification_cmd(uint8_t label, uint8_t event_id,
                                              uint32_t event_value,
                                              btif_rc_device_cb_t* p_dev);
+static bt_status_t get_metadata_attribute_cmd(uint8_t num_attribute,
+                                              const uint32_t* p_attr_ids,
+                                              btif_rc_device_cb_t* p_dev);
 static bt_status_t get_element_attribute_cmd(uint8_t num_attribute,
-                                             uint32_t* p_attr_ids,
+                                             const uint32_t* p_attr_ids,
                                              btif_rc_device_cb_t* p_dev);
+static bt_status_t get_item_attribute_cmd(uint64_t uid, int scope,
+                                           uint8_t num_attribute,
+                                           const uint32_t* p_attr_ids,
+                                           btif_rc_device_cb_t* p_dev);
 static bt_status_t getcapabilities_cmd(uint8_t cap_id,
                                        btif_rc_device_cb_t* p_dev);
 static bt_status_t list_player_app_setting_attrib_cmd(
@@ -346,6 +354,26 @@
 static btrc_callbacks_t* bt_rc_callbacks = NULL;
 static btrc_ctrl_callbacks_t* bt_rc_ctrl_callbacks = NULL;
 
+// List of desired media attribute keys to request by default
+static const uint32_t media_attr_list[] = {
+      AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
+      AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
+      AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
+      AVRC_MEDIA_ATTR_ID_PLAYING_TIME,
+      AVRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE};
+static const uint8_t media_attr_list_size =
+    sizeof(media_attr_list)/sizeof(uint32_t);
+
+// List of desired media attribute keys to request if cover artwork is not a
+// supported feature
+static const uint32_t media_attr_list_no_cover_art[] = {
+      AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
+      AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
+      AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
+      AVRC_MEDIA_ATTR_ID_PLAYING_TIME};
+static const uint8_t media_attr_list_no_cover_art_size =
+    sizeof(media_attr_list_no_cover_art)/sizeof(uint32_t);
+
 /*****************************************************************************
  *  Static functions
  *****************************************************************************/
@@ -425,6 +453,18 @@
   return NULL;
 }
 
+const uint32_t* get_requested_attributes_list(btif_rc_device_cb_t* p_dev) {
+  return (p_dev->rc_features & BTA_AV_FEAT_COVER_ARTWORK
+      ? media_attr_list
+      : media_attr_list_no_cover_art);
+}
+
+uint8_t get_requested_attributes_list_size(btif_rc_device_cb_t* p_dev) {
+  return (p_dev->rc_features & BTA_AV_FEAT_COVER_ARTWORK
+      ? media_attr_list_size
+      : media_attr_list_no_cover_art_size);
+}
+
 void fill_pdu_queue(int index, uint8_t ctype, uint8_t label, bool pending,
                     btif_rc_device_cb_t* p_dev) {
   p_dev->rc_pdu_info[index].ctype = ctype;
@@ -485,11 +525,25 @@
     rc_features |= BTRC_FEAT_BROWSE;
   }
 
+  /* Add cover art feature capability */
+  if (p_dev->rc_features & BTA_AV_FEAT_COVER_ARTWORK) {
+    rc_features |= BTRC_FEAT_COVER_ARTWORK;
+  }
+
   BTIF_TRACE_DEBUG("%s: Update rc features to CTRL: %d", __func__, rc_features);
   do_in_jni_thread(FROM_HERE, base::Bind(bt_rc_ctrl_callbacks->getrcfeatures_cb,
                                          p_dev->rc_addr, rc_features));
 }
 
+void handle_rc_ctrl_psm(btif_rc_device_cb_t* p_dev) {
+  uint16_t cover_art_psm = p_dev->rc_cover_art_psm;
+  BTIF_TRACE_DEBUG("%s: Update rc cover art psm to CTRL: %d", __func__,
+      cover_art_psm);
+  do_in_jni_thread(FROM_HERE, base::Bind(
+      bt_rc_ctrl_callbacks->get_cover_art_psm_cb,
+      p_dev->rc_addr, cover_art_psm));
+}
+
 void handle_rc_features(btif_rc_device_cb_t* p_dev) {
 
   CHECK(bt_rc_callbacks);
@@ -631,6 +685,9 @@
   p_dev->rc_features = p_rc_open->peer_features;
   BTIF_TRACE_DEBUG("%s: handle_rc_connect in features: 0x%x out features 0x%x",
                    __func__, p_rc_open->peer_features, p_dev->rc_features);
+  p_dev->rc_cover_art_psm = p_rc_open->cover_art_psm;
+  BTIF_TRACE_DEBUG("%s: cover art psm: 0x%x",
+                   __func__, p_dev->rc_cover_art_psm);
   p_dev->rc_vol_label = MAX_LABEL;
   p_dev->rc_volume = MAX_VOLUME;
 
@@ -651,6 +708,9 @@
   }
   /* report connection state if remote device is AVRCP target */
   handle_rc_ctrl_features(p_dev);
+
+  /* report psm if remote device is AVRCP target */
+  handle_rc_ctrl_psm(p_dev);
 }
 
 /***************************************************************************
@@ -701,6 +761,7 @@
     memset(p_dev->rc_notif, 0, sizeof(p_dev->rc_notif));
 
     p_dev->rc_features = 0;
+    p_dev->rc_cover_art_psm = 0;
     p_dev->rc_vol_label = MAX_LABEL;
     p_dev->rc_volume = MAX_VOLUME;
 
@@ -974,8 +1035,9 @@
   btif_rc_device_cb_t* p_dev = NULL;
   switch (event) {
     case BTA_AV_RC_OPEN_EVT: {
-      BTIF_TRACE_DEBUG("%s: Peer_features: %x", __func__,
-                       p_data->rc_open.peer_features);
+      BTIF_TRACE_DEBUG("%s: Peer_features: 0x%x Cover Art PSM: 0x%x", __func__,
+                       p_data->rc_open.peer_features,
+                       p_data->rc_open.cover_art_psm);
       handle_rc_connect(&(p_data->rc_open));
     } break;
 
@@ -1036,6 +1098,22 @@
       }
     } break;
 
+    case BTA_AV_RC_PSM_EVT: {
+      BTIF_TRACE_DEBUG("%s: Peer cover art PSM: %x", __func__,
+                       p_data->rc_cover_art_psm.cover_art_psm);
+      p_dev = btif_rc_get_device_by_handle(p_data->rc_cover_art_psm.rc_handle);
+      if (p_dev == NULL) {
+        BTIF_TRACE_ERROR("%s: RC PSM event for Invalid rc handle",
+                         __func__);
+        break;
+      }
+
+      p_dev->rc_cover_art_psm = p_data->rc_cover_art_psm.cover_art_psm;
+      if ((p_dev->rc_connected) && (bt_rc_ctrl_callbacks != NULL)) {
+        handle_rc_ctrl_psm(p_dev);
+      }
+    } break;
+
     case BTA_AV_META_MSG_EVT: {
       if (bt_rc_callbacks != NULL) {
         BTIF_TRACE_DEBUG("%s: BTA_AV_META_MSG_EVT code: %d label: %d", __func__,
@@ -1750,6 +1828,7 @@
            sizeof(btif_rc_cb.rc_multi_cb[idx]));
     btif_rc_cb.rc_multi_cb[idx].rc_vol_label = MAX_LABEL;
     btif_rc_cb.rc_multi_cb[idx].rc_volume = MAX_VOLUME;
+    btif_rc_cb.rc_multi_cb[idx].rc_features_processed = FALSE;
   }
   lbl_init();
 
@@ -1766,12 +1845,9 @@
     return;
   }
   p_dev->rc_procedure_complete = true;
-  uint32_t attr_list[] = {
-      AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
-      AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
-      AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
-      AVRC_MEDIA_ATTR_ID_PLAYING_TIME};
-  get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list, p_dev);
+  const uint32_t* attr_list = get_requested_attributes_list(p_dev);
+  const uint8_t attr_list_size = get_requested_attributes_list_size(p_dev);
+  get_metadata_attribute_cmd(attr_list_size, attr_list, p_dev);
 }
 
 /***************************************************************************
@@ -1836,6 +1912,12 @@
   BTIF_TRACE_DEBUG("%s", __func__);
   CHECK_RC_CONNECTED(p_dev);
 
+  if (num_attr > BTRC_MAX_ELEM_ATTR_SIZE) {
+    LOG(WARNING) << __func__
+                 << " Exceeded number attributes:" << static_cast<int>(num_attr)
+                 << " max:" << BTRC_MAX_ELEM_ATTR_SIZE;
+    num_attr = BTRC_MAX_ELEM_ATTR_SIZE;
+  }
   memset(element_attrs, 0, sizeof(tAVRC_ATTR_ENTRY) * num_attr);
 
   if (num_attr == 0) {
@@ -1844,7 +1926,8 @@
     for (i = 0; i < num_attr; i++) {
       element_attrs[i].attr_id = p_attrs[i].attr_id;
       element_attrs[i].name.charset_id = AVRC_CHARSET_ID_UTF8;
-      element_attrs[i].name.str_len = (uint16_t)strlen((char*)p_attrs[i].text);
+      element_attrs[i].name.str_len =
+          (uint16_t)strnlen((char*)p_attrs[i].text, BTRC_MAX_ATTR_STR_LEN);
       element_attrs[i].name.p_str = p_attrs[i].text;
       BTIF_TRACE_DEBUG(
           "%s: attr_id: 0x%x, charset_id: 0x%x, str_len: %d, str: %s", __func__,
@@ -2844,7 +2927,7 @@
 
     case AVRC_PDU_GET_ELEMENT_ATTR:
       avrc_response.get_attrs.status = BTIF_RC_STS_TIMEOUT;
-      handle_get_elem_attr_response(&meta_msg, &avrc_response.get_attrs);
+      handle_get_metadata_attr_response(&meta_msg, &avrc_response.get_attrs);
       break;
 
     case AVRC_PDU_GET_PLAY_STATUS:
@@ -3022,6 +3105,44 @@
 
 /***************************************************************************
  *
+ * Function         send_browsing_command
+ *
+ * Description      Send a command to a device on the browsing channel
+ *
+ * Parameters       avrc_cmd: The command you're sending
+ *                  p_dev: Device control block
+ *
+ * Returns          BT_STATUS_SUCCESS if command is issued successfully
+ *                  otherwise BT_STATUS_FAIL
+ *
+ **************************************************************************/
+static bt_status_t build_and_send_browsing_cmd(tAVRC_COMMAND* avrc_cmd,
+                                         btif_rc_device_cb_t* p_dev) {
+  BT_HDR* p_msg = NULL;
+  tAVRC_STS status = AVRC_BldCommand(avrc_cmd, &p_msg);
+  if (status != AVRC_STS_NO_ERROR) {
+    BTIF_TRACE_ERROR("%s: failed to build command status %d", __func__, status);
+    return BT_STATUS_FAIL;
+  }
+
+  rc_transaction_t* p_transaction = NULL;
+  bt_status_t tran_status = get_transaction(&p_transaction);
+
+  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
+    osi_free(p_msg);
+    BTIF_TRACE_ERROR("%s: failed to obtain txn details. status: 0x%02x",
+                     __func__, tran_status);
+    return BT_STATUS_FAIL;
+  }
+
+  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
+                   p_transaction->lbl);
+  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
+  return BT_STATUS_SUCCESS;
+}
+
+/***************************************************************************
+ *
  * Function         handle_get_capability_response
  *
  * Description      Handles the get_cap_response to populate company id info
@@ -3056,7 +3177,8 @@
           (p_rsp->param.event_id[xx] == AVRC_EVT_APP_SETTING_CHANGE) ||
           (p_rsp->param.event_id[xx] == AVRC_EVT_NOW_PLAYING_CHANGE) ||
           (p_rsp->param.event_id[xx] == AVRC_EVT_ADDR_PLAYER_CHANGE) ||
-          (p_rsp->param.event_id[xx] == AVRC_EVT_UIDS_CHANGE)) {
+          (p_rsp->param.event_id[xx] == AVRC_EVT_UIDS_CHANGE) ||
+          (p_rsp->param.event_id[xx] == AVRC_EVT_AVAL_PLAYERS_CHANGE)) {
         p_event = (btif_rc_supported_event_t*)osi_malloc(
             sizeof(btif_rc_supported_event_t));
         p_event->event_id = p_rsp->param.event_id[xx];
@@ -3121,17 +3243,14 @@
                                          tAVRC_REG_NOTIF_RSP* p_rsp) {
   btif_rc_device_cb_t* p_dev =
       btif_rc_get_device_by_handle(pmeta_msg->rc_handle);
-  uint32_t attr_list[] = {
-      AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
-      AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
-      AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
-      AVRC_MEDIA_ATTR_ID_PLAYING_TIME};
 
   if (p_dev == NULL) {
     BTIF_TRACE_ERROR("%s: p_dev NULL", __func__);
     return;
   }
 
+  const uint32_t* attr_list = get_requested_attributes_list(p_dev);
+  const uint8_t attr_list_size = get_requested_attributes_list_size(p_dev);
 
   if (pmeta_msg->code == AVRC_RSP_INTERIM) {
     btif_rc_supported_event_t* p_event;
@@ -3155,7 +3274,7 @@
           uint8_t* p_data = p_rsp->param.track;
           BE_STREAM_TO_UINT64(p_dev->rc_playing_uid, p_data);
           get_play_status_cmd(p_dev);
-          get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list,
+          get_metadata_attribute_cmd(attr_list_size, attr_list,
                                     p_dev);
         }
         break;
@@ -3171,6 +3290,11 @@
         break;
 
       case AVRC_EVT_AVAL_PLAYERS_CHANGE:
+        BTIF_TRACE_DEBUG("%s: AVRC_EVT_AVAL_PLAYERS_CHANGE", __func__);
+        do_in_jni_thread(
+            FROM_HERE,
+            base::Bind(bt_rc_ctrl_callbacks->available_player_changed_cb,
+                       p_dev->rc_addr));
         break;
 
       case AVRC_EVT_ADDR_PLAYER_CHANGE:
@@ -3251,10 +3375,6 @@
         /* Start timer to get play status periodically
          * if the play state is playing.
          */
-        if (p_rsp->param.play_status == AVRC_PLAYSTATE_PLAYING) {
-          get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list,
-                                    p_dev);
-        }
         do_in_jni_thread(
             FROM_HERE,
             base::Bind(bt_rc_ctrl_callbacks->play_status_changed_cb,
@@ -3267,7 +3387,7 @@
         if (rc_is_track_id_valid(p_rsp->param.track) != true) {
           break;
         }
-        get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list, p_dev);
+        get_metadata_attribute_cmd(attr_list_size, attr_list, p_dev);
         break;
 
       case AVRC_EVT_APP_SETTING_CHANGE: {
@@ -3735,14 +3855,14 @@
 
 /***************************************************************************
  *
- * Function         handle_get_elem_attr_response
+ * Function         handle_get_metadata_attr_response
  *
  * Description      handles the the element attributes response, calls
  *                  HAL callback to update track change information.
  * Returns          None
  *
  **************************************************************************/
-static void handle_get_elem_attr_response(tBTA_AV_META_MSG* pmeta_msg,
+static void handle_get_metadata_attr_response(tBTA_AV_META_MSG* pmeta_msg,
                                           tAVRC_GET_ATTRS_RSP* p_rsp) {
   btif_rc_device_cb_t* p_dev =
       btif_rc_get_device_by_handle(pmeta_msg->rc_handle);
@@ -3775,12 +3895,9 @@
     /* Retry for timeout case, this covers error handling
      * for continuation failure also.
      */
-    uint32_t attr_list[] = {
-        AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
-        AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
-        AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
-        AVRC_MEDIA_ATTR_ID_PLAYING_TIME};
-    get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list, p_dev);
+    const uint32_t* attr_list = get_requested_attributes_list(p_dev);
+    const uint8_t attr_list_size = get_requested_attributes_list_size(p_dev);
+    get_metadata_attribute_cmd(attr_list_size, attr_list, p_dev);
   } else {
     BTIF_TRACE_ERROR("%s: Error in get element attr procedure: %d", __func__,
                      p_rsp->status);
@@ -4030,6 +4147,9 @@
       case AVRC_MEDIA_ATTR_ID_PLAYING_TIME:
         btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_PLAYING_TIME;
         break;
+      case AVRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE:
+        btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE;
+        break;
       default:
         BTIF_TRACE_ERROR("%s invalid media attr id: 0x%x", __func__,
                          avrc_attr_pair->attr_id);
@@ -4267,7 +4387,7 @@
         break;
 
       case AVRC_PDU_GET_ELEMENT_ATTR:
-        handle_get_elem_attr_response(pmeta_msg, &avrc_response.get_attrs);
+        handle_get_metadata_attr_response(pmeta_msg, &avrc_response.get_attrs);
         break;
 
       case AVRC_PDU_GET_PLAY_STATUS:
@@ -4292,6 +4412,9 @@
       case AVRC_PDU_SET_BROWSED_PLAYER:
         handle_set_browsed_player_response(pmeta_msg, &avrc_response.br_player);
         break;
+      case AVRC_PDU_GET_ITEM_ATTRIBUTES:
+        handle_get_metadata_attr_response(pmeta_msg, &avrc_response.get_attrs);
+        break;
       default:
         BTIF_TRACE_ERROR("%s cannot handle browse pdu %d", __func__,
                          pmeta_msg->p_msg->hdr.opcode);
@@ -4510,6 +4633,28 @@
 
 /***************************************************************************
  *
+ * Function         get_current_metadata_cmd
+ *
+ * Description      Fetch the current track metadata for the device
+ *
+ * Returns          BT_STATUS_SUCCESS if command issued successfully otherwise
+ *                  BT_STATUS_FAIL.
+ *
+ **************************************************************************/
+static bt_status_t get_current_metadata_cmd(const RawAddress& bd_addr) {
+  BTIF_TRACE_DEBUG("%s", __func__);
+  btif_rc_device_cb_t* p_dev = btif_rc_get_device_by_bda(bd_addr);
+  if (p_dev == NULL) {
+    BTIF_TRACE_ERROR("%s: p_dev NULL", __func__);
+    return BT_STATUS_FAIL;
+  }
+  const uint32_t* attr_list = get_requested_attributes_list(p_dev);
+  const uint8_t attr_list_size = get_requested_attributes_list_size(p_dev);
+  return get_metadata_attribute_cmd(attr_list_size, attr_list, p_dev);
+}
+
+/***************************************************************************
+ *
  * Function         get_playback_state_cmd
  *
  * Description      Fetch the current playback state for the device
@@ -4547,6 +4692,35 @@
 
 /***************************************************************************
  *
+ * Function         get_item_attribute_cmd
+ *
+ * Description      Fetch the item attributes for a given uid.
+ *
+ * Parameters       uid: Track UID you want attributes for
+ *                  scope: Constant representing which scope you're querying
+ *                         (i.e AVRC_SCOPE_FILE_SYSTEM)
+ *                  p_dev: Device control block
+ *
+ * Returns          BT_STATUS_SUCCESS if command is issued successfully
+ *                  otherwise BT_STATUS_FAIL
+ *
+ **************************************************************************/
+static bt_status_t get_item_attribute_cmd(uint64_t uid, int scope,
+                                           uint8_t num_attribute,
+                                           const uint32_t* p_attr_ids,
+                                           btif_rc_device_cb_t* p_dev) {
+  tAVRC_COMMAND avrc_cmd = {0};
+  avrc_cmd.pdu = AVRC_PDU_GET_ITEM_ATTRIBUTES;
+  avrc_cmd.get_attrs.scope = scope;
+  memcpy(avrc_cmd.get_attrs.uid, &uid, 8);
+  avrc_cmd.get_attrs.uid_counter = 0;
+  avrc_cmd.get_attrs.attr_count = 0;
+
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
+}
+
+/***************************************************************************
+ *
  * Function         get_folder_list_cmd
  *
  * Description      Fetch the currently selected folder list
@@ -4618,26 +4792,7 @@
   memset(avrc_cmd.chg_path.folder_uid, 0, AVRC_UID_SIZE * sizeof(uint8_t));
   memcpy(avrc_cmd.chg_path.folder_uid, uid, AVRC_UID_SIZE * sizeof(uint8_t));
 
-  BT_HDR* p_msg = NULL;
-  tAVRC_STS status = AVRC_BldCommand(&avrc_cmd, &p_msg);
-  if (status != AVRC_STS_NO_ERROR) {
-    BTIF_TRACE_ERROR("%s failed to build command status %d", __func__, status);
-    return BT_STATUS_FAIL;
-  }
-
-  rc_transaction_t* p_transaction = NULL;
-  bt_status_t tran_status = get_transaction(&p_transaction);
-  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
-    osi_free(p_msg);
-    BTIF_TRACE_ERROR("%s: failed to obtain transaction details. status: 0x%02x",
-                     __func__, tran_status);
-    return BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
-                   p_transaction->lbl);
-  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
-  return BT_STATUS_SUCCESS;
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
 }
 
 /***************************************************************************
@@ -4659,33 +4814,13 @@
   CHECK_RC_CONNECTED(p_dev);
   CHECK_BR_CONNECTED(p_dev);
 
-  rc_transaction_t* p_transaction = NULL;
-
   tAVRC_COMMAND avrc_cmd = {0};
   avrc_cmd.br_player.pdu = AVRC_PDU_SET_BROWSED_PLAYER;
   avrc_cmd.br_player.status = AVRC_STS_NO_ERROR;
   // TODO(sanketa): Improve for database aware clients.
   avrc_cmd.br_player.player_id = id;
 
-  BT_HDR* p_msg = NULL;
-  tAVRC_STS status = AVRC_BldCommand(&avrc_cmd, &p_msg);
-  if (status != AVRC_STS_NO_ERROR) {
-    BTIF_TRACE_ERROR("%s failed to build command status %d", __func__, status);
-    return BT_STATUS_FAIL;
-  }
-
-  bt_status_t tran_status = get_transaction(&p_transaction);
-  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
-    osi_free(p_msg);
-    BTIF_TRACE_ERROR("%s: failed to obtain transaction details. status: 0x%02x",
-                     __func__, tran_status);
-    return BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
-                   p_transaction->lbl);
-  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
-  return BT_STATUS_SUCCESS;
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
 }
 
 /***************************************************************************
@@ -4709,33 +4844,12 @@
   CHECK_BR_CONNECTED(p_dev);
 
   tAVRC_COMMAND avrc_cmd = {0};
-  BT_HDR* p_msg = NULL;
-
   avrc_cmd.addr_player.pdu = AVRC_PDU_SET_ADDRESSED_PLAYER;
   avrc_cmd.addr_player.status = AVRC_STS_NO_ERROR;
   // TODO(sanketa): Improve for database aware clients.
   avrc_cmd.addr_player.player_id = id;
 
-  tAVRC_STS status = AVRC_BldCommand(&avrc_cmd, &p_msg);
-  if (status != AVRC_STS_NO_ERROR) {
-    BTIF_TRACE_ERROR("%s: failed to build command status %d", __func__, status);
-    return BT_STATUS_FAIL;
-  }
-
-  rc_transaction_t* p_transaction = NULL;
-  bt_status_t tran_status = get_transaction(&p_transaction);
-
-  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
-    osi_free(p_msg);
-    BTIF_TRACE_ERROR("%s: failed to obtain txn details. status: 0x%02x",
-                     __func__, tran_status);
-    return BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
-                   p_transaction->lbl);
-  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
-  return BT_STATUS_SUCCESS;
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
 }
 
 /***************************************************************************
@@ -4775,26 +4889,7 @@
   avrc_cmd.get_items.end_item = end_item;
   avrc_cmd.get_items.attr_count = 0; /* p_attr_list does not matter hence */
 
-  BT_HDR* p_msg = NULL;
-  tAVRC_STS status = AVRC_BldCommand(&avrc_cmd, &p_msg);
-  if (status != AVRC_STS_NO_ERROR) {
-    BTIF_TRACE_ERROR("%s failed to build command status %d", __func__, status);
-    return BT_STATUS_FAIL;
-  }
-
-  rc_transaction_t* p_transaction = NULL;
-  bt_status_t tran_status = get_transaction(&p_transaction);
-  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
-    osi_free(p_msg);
-    BTIF_TRACE_ERROR("%s: failed to obtain transaction details. status: 0x%02x",
-                     __func__, tran_status);
-    return BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
-                   p_transaction->lbl);
-  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
-  return BT_STATUS_SUCCESS;
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
 }
 
 /***************************************************************************
@@ -4855,7 +4950,8 @@
   memcpy(avrc_cmd.play_item.uid, uid, AVRC_UID_SIZE);
   avrc_cmd.play_item.uid_counter = uid_counter;
 
-  return build_and_send_vendor_cmd(&avrc_cmd, AVRC_CMD_CTRL, p_dev);
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
+  // return build_and_send_vendor_cmd(&avrc_cmd, AVRC_CMD_CTRL, p_dev);
 }
 
 /***************************************************************************
@@ -4954,6 +5050,35 @@
 
 /***************************************************************************
  *
+ * Function         get_metadata_attribute_cmd
+ *
+ * Description      Get metadata attributes for attributeIds. This function
+ *                  will make the right determination of whether to use the
+ *                  control or browsing channel for the request
+ *
+ * Returns          BT_STATUS_SUCCESS if the command is successfully issued
+ *                  otherwise BT_STATUS_FAIL
+ *
+ **************************************************************************/
+static bt_status_t get_metadata_attribute_cmd(uint8_t num_attribute,
+                                              const uint32_t* p_attr_ids,
+                                              btif_rc_device_cb_t* p_dev) {
+  BTIF_TRACE_DEBUG("%s: num_attribute: %d attribute_id: %d", __func__,
+                   num_attribute, p_attr_ids[0]);
+
+  // If browsing is connected then send the command out that channel
+  if (p_dev->br_connected) {
+    return get_item_attribute_cmd(p_dev->rc_playing_uid,
+                                   AVRC_SCOPE_NOW_PLAYING, num_attribute,
+                                   p_attr_ids, p_dev);
+  }
+
+  // Otherwise, default to the control channel
+  return get_element_attribute_cmd(num_attribute, p_attr_ids, p_dev);
+}
+
+/***************************************************************************
+ *
  * Function         get_element_attribute_cmd
  *
  * Description      Get Element Attribute for  attributeIds
@@ -4962,12 +5087,11 @@
  *
  **************************************************************************/
 static bt_status_t get_element_attribute_cmd(uint8_t num_attribute,
-                                             uint32_t* p_attr_ids,
+                                             const uint32_t* p_attr_ids,
                                              btif_rc_device_cb_t* p_dev) {
   BTIF_TRACE_DEBUG("%s: num_attribute: %d attribute_id: %d", __func__,
                    num_attribute, p_attr_ids[0]);
   CHECK_RC_CONNECTED(p_dev);
-
   tAVRC_COMMAND avrc_cmd = {0};
   avrc_cmd.get_elem_attrs.opcode = AVRC_OP_VENDOR;
   avrc_cmd.get_elem_attrs.status = AVRC_STS_NO_ERROR;
@@ -5207,6 +5331,7 @@
     send_groupnavigation_cmd,
     change_player_app_setting,
     play_item_cmd,
+    get_current_metadata_cmd,
     get_playback_state_cmd,
     get_now_playing_list_cmd,
     get_folder_list_cmd,
diff --git a/btif/src/btif_sock.cc b/btif/src/btif_sock.cc
index 8c4c9c3..312ea94 100644
--- a/btif/src/btif_sock.cc
+++ b/btif/src/btif_sock.cc
@@ -74,35 +74,32 @@
   btsock_thread_init();
   thread_handle = btsock_thread_create(btsock_signaled, NULL);
   if (thread_handle == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to create btsock_thread.", __func__);
+    LOG_ERROR("%s unable to create btsock_thread.", __func__);
     goto error;
   }
 
   status = btsock_rfc_init(thread_handle, uid_set);
   if (status != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s error initializing RFCOMM sockets: %d", __func__,
-              status);
+    LOG_ERROR("%s error initializing RFCOMM sockets: %d", __func__, status);
     goto error;
   }
 
   status = btsock_l2cap_init(thread_handle, uid_set);
   if (status != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s error initializing L2CAP sockets: %d", __func__,
-              status);
+    LOG_ERROR("%s error initializing L2CAP sockets: %d", __func__, status);
     goto error;
   }
 
   thread = thread_new("btif_sock");
   if (!thread) {
-    LOG_ERROR(LOG_TAG, "%s error creating new thread.", __func__);
+    LOG_ERROR("%s error creating new thread.", __func__);
     btsock_rfc_cleanup();
     goto error;
   }
 
   status = btsock_sco_init(thread);
   if (status != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s error initializing SCO sockets: %d", __func__,
-              status);
+    LOG_ERROR("%s error initializing SCO sockets: %d", __func__, status);
     btsock_rfc_cleanup();
     goto error;
   }
@@ -160,13 +157,13 @@
         /* Set channel to zero so that it will be assigned */
         channel = 0;
       } else if (channel <= 0) {
-        LOG_ERROR(LOG_TAG, "%s: type BTSOCK_L2CAP_LE: invalid channel=%d",
-                  __func__, channel);
+        LOG_ERROR("%s: type BTSOCK_L2CAP_LE: invalid channel=%d", __func__,
+                  channel);
         break;
       }
       flags |= BTSOCK_FLAG_LE_COC;
       LOG_DEBUG(
-          LOG_TAG,
+
           "%s: type=BTSOCK_L2CAP_LE, channel=0x%x, original=0x%x, flags=0x%x",
           __func__, channel, original_channel, flags);
       status =
@@ -177,8 +174,7 @@
       break;
 
     default:
-      LOG_ERROR(LOG_TAG, "%s unknown/unsupported socket type: %d", __func__,
-                type);
+      LOG_ERROR("%s unknown/unsupported socket type: %d", __func__, type);
       status = BT_STATUS_UNSUPPORTED;
       break;
   }
@@ -229,8 +225,8 @@
         BTA_DmAddBleDevice(*bd_addr, addr_type, device_type);
       }
 
-      LOG_DEBUG(LOG_TAG, "%s: type=BTSOCK_L2CAP_LE, channel=0x%x, flags=0x%x",
-                __func__, channel, flags);
+      LOG_DEBUG("%s: type=BTSOCK_L2CAP_LE, channel=0x%x, flags=0x%x", __func__,
+                channel, flags);
       status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid);
       break;
     }
@@ -240,8 +236,7 @@
       break;
 
     default:
-      LOG_ERROR(LOG_TAG, "%s unknown/unsupported socket type: %d", __func__,
-                type);
+      LOG_ERROR("%s unknown/unsupported socket type: %d", __func__, type);
       status = BT_STATUS_UNSUPPORTED;
       break;
   }
diff --git a/btif/src/btif_sock_l2cap.cc b/btif/src/btif_sock_l2cap.cc
index 1f85c73..105d5a1 100644
--- a/btif/src/btif_sock_l2cap.cc
+++ b/btif/src/btif_sock_l2cap.cc
@@ -97,6 +97,7 @@
 static std::mutex state_lock;
 
 l2cap_socket* socks = NULL;
+static uint32_t last_sock_id = 0;
 static uid_set_t* uid_set = NULL;
 static int pth = -1;
 
@@ -254,6 +255,11 @@
     }
     if ((sock->channel >= 0) && (sock->server)) {
       BTA_JvFreeChannel(sock->channel, BTA_JV_CONN_TYPE_L2CAP_LE);
+      if (!sock->fixed_chan) {
+        VLOG(2) << __func__ << ": stopping L2CAP LE COC server channel "
+                << sock->channel;
+        BTA_JvL2capStopServer(sock->channel, sock->id);
+      }
     }
   } else {
     // Only call if we are non server connections
@@ -322,7 +328,7 @@
   sock->next = socks;
   sock->prev = NULL;
   if (socks) socks->prev = sock;
-  sock->id = (socks ? socks->id : 0) + 1;
+  sock->id = last_sock_id + 1;
   sock->tx_bytes = 0;
   sock->rx_bytes = 0;
   socks = sock;
@@ -340,6 +346,7 @@
     if (!++sock->id) /* no zero IDs allowed */
       sock->id++;
   }
+  last_sock_id = sock->id;
   DVLOG(2) << __func__ << " SOCK_LIST: alloc id:" << sock->id;
   return sock;
 
diff --git a/btif/src/btif_sock_rfc.cc b/btif/src/btif_sock_rfc.cc
index 3c9e26e..a451e3e 100644
--- a/btif/src/btif_sock_rfc.cc
+++ b/btif/src/btif_sock_rfc.cc
@@ -165,7 +165,7 @@
   for (size_t i = 0; i < ARRAY_SIZE(rfc_slots); ++i)
     if (rfc_slots[i].id == id) return &rfc_slots[i];
 
-  LOG_ERROR(LOG_TAG, "%s unable to find RFCOMM slot id: %u", __func__, id);
+  LOG_ERROR("%s unable to find RFCOMM slot id: %u", __func__, id);
   return NULL;
 }
 
@@ -203,14 +203,13 @@
 
   rfc_slot_t* slot = find_free_slot();
   if (!slot) {
-    LOG_ERROR(LOG_TAG, "%s unable to find free RFCOMM slot.", __func__);
+    LOG_ERROR("%s unable to find free RFCOMM slot.", __func__);
     return NULL;
   }
 
   int fds[2] = {INVALID_FD, INVALID_FD};
   if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) == -1) {
-    LOG_ERROR(LOG_TAG, "%s error creating socketpair: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s error creating socketpair: %s", __func__, strerror(errno));
     return NULL;
   }
 
@@ -250,7 +249,7 @@
   rfc_slot_t* accept_rs = alloc_rfc_slot(
       addr, srv_rs->service_name, srv_rs->service_uuid, srv_rs->scn, 0, false);
   if (!accept_rs) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate RFCOMM slot.", __func__);
+    LOG_ERROR("%s unable to allocate RFCOMM slot.", __func__);
     return NULL;
   }
 
@@ -314,7 +313,7 @@
   rfc_slot_t* slot =
       alloc_rfc_slot(NULL, service_name, *service_uuid, channel, flags, true);
   if (!slot) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate RFCOMM slot.", __func__);
+    LOG_ERROR("%s unable to allocate RFCOMM slot.", __func__);
     return BT_STATUS_FAIL;
   }
   APPL_TRACE_DEBUG("BTA_JvGetChannelId: service_name: %s - channel: %d",
@@ -356,7 +355,7 @@
   rfc_slot_t* slot =
       alloc_rfc_slot(bd_addr, NULL, *service_uuid, channel, flags, false);
   if (!slot) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate RFCOMM slot.", __func__);
+    LOG_ERROR("%s unable to allocate RFCOMM slot.", __func__);
     return BT_STATUS_FAIL;
   }
 
@@ -365,14 +364,13 @@
         BTA_JvRfcommConnect(slot->security, slot->role, slot->scn, slot->addr,
                             rfcomm_cback, slot->id);
     if (ret != BTA_JV_SUCCESS) {
-      LOG_ERROR(LOG_TAG, "%s unable to initiate RFCOMM connection: %d",
-                __func__, ret);
+      LOG_ERROR("%s unable to initiate RFCOMM connection: %d", __func__, ret);
       cleanup_rfc_slot(slot);
       return BT_STATUS_FAIL;
     }
 
     if (!send_app_scn(slot)) {
-      LOG_ERROR(LOG_TAG, "%s unable to send channel number.", __func__);
+      LOG_ERROR("%s unable to send channel number.", __func__);
       cleanup_rfc_slot(slot);
       return BT_STATUS_FAIL;
     }
@@ -567,7 +565,7 @@
   if (send_app_connect_signal(slot->fd, &slot->addr, slot->scn, 0, -1)) {
     slot->f.connected = true;
   } else {
-    LOG_ERROR(LOG_TAG, "%s unable to send connect completion signal to caller.",
+    LOG_ERROR("%s unable to send connect completion signal to caller.",
               __func__);
   }
 }
@@ -591,8 +589,8 @@
 
 static void on_rfc_write_done(tBTA_JV_RFCOMM_WRITE* p, uint32_t id) {
   if (p->status != BTA_JV_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s error writing to RFCOMM socket with slot %u.",
-              __func__, p->req_id);
+    LOG_ERROR("%s error writing to RFCOMM socket with slot %u.", __func__,
+              p->req_id);
     return;
   }
 
@@ -668,7 +666,7 @@
       break;
 
     default:
-      LOG_ERROR(LOG_TAG, "%s unhandled event %d, slot id: %u", __func__, event,
+      LOG_ERROR("%s unhandled event %d, slot id: %u", __func__, event,
                 rfcomm_slot_id);
       break;
   }
@@ -763,10 +761,10 @@
         } else if (slot) {
           // TODO(sharvil): this is really a logic error and we should probably
           // assert.
-          LOG_ERROR(LOG_TAG,
-                    "%s SDP response returned but RFCOMM slot %d did not "
-                    "request SDP record.",
-                    __func__, id);
+          LOG_ERROR(
+              "%s SDP response returned but RFCOMM slot %d did not "
+              "request SDP record.",
+              __func__, id);
         }
       } else if (slot) {
         cleanup_rfc_slot(slot);
@@ -804,7 +802,7 @@
 
   if (sent == -1) {
     if (errno == EAGAIN || errno == EWOULDBLOCK) return SENT_NONE;
-    LOG_ERROR(LOG_TAG, "%s error writing RFCOMM data back to app: %s", __func__,
+    LOG_ERROR("%s error writing RFCOMM data back to app: %s", __func__,
               strerror(errno));
     return SENT_FAILED;
   }
@@ -864,10 +862,10 @@
         BTA_JvRfcommWrite(slot->rfc_handle, slot->id);
       }
     } else {
-      LOG_ERROR(LOG_TAG,
-                "%s socket signaled for read while disconnected, slot: %d, "
-                "channel: %d",
-                __func__, slot->id, slot->scn);
+      LOG_ERROR(
+          "%s socket signaled for read while disconnected, slot: %d, "
+          "channel: %d",
+          __func__, slot->id, slot->scn);
       need_close = true;
     }
   }
@@ -875,10 +873,10 @@
   if (flags & SOCK_THREAD_FD_WR) {
     // App is ready to receive more data, tell stack to enable data flow.
     if (!slot->f.connected || !flush_incoming_que_on_wr_signal(slot)) {
-      LOG_ERROR(LOG_TAG,
-                "%s socket signaled for write while disconnected (or write "
-                "failure), slot: %d, channel: %d",
-                __func__, slot->id, slot->scn);
+      LOG_ERROR(
+          "%s socket signaled for write while disconnected (or write "
+          "failure), slot: %d, channel: %d",
+          __func__, slot->id, slot->scn);
       need_close = true;
     }
   }
@@ -938,8 +936,7 @@
   if (!slot) return false;
 
   if (ioctl(slot->fd, FIONREAD, size) != 0) {
-    LOG_ERROR(LOG_TAG,
-              "%s unable to determine bytes remaining to be read on fd %d: %s",
+    LOG_ERROR("%s unable to determine bytes remaining to be read on fd %d: %s",
               __func__, slot->fd, strerror(errno));
     cleanup_rfc_slot(slot);
     return false;
@@ -957,7 +954,7 @@
   OSI_NO_INTR(received = recv(slot->fd, buf, size, 0));
 
   if (received != size) {
-    LOG_ERROR(LOG_TAG, "%s error receiving RFCOMM data from app: %s", __func__,
+    LOG_ERROR("%s error receiving RFCOMM data from app: %s", __func__,
               strerror(errno));
     cleanup_rfc_slot(slot);
     return false;
diff --git a/btif/src/btif_sock_sco.cc b/btif/src/btif_sock_sco.cc
index 6283196..63d9e1b 100644
--- a/btif/src/btif_sock_sco.cc
+++ b/btif/src/btif_sock_sco.cc
@@ -138,14 +138,14 @@
   tBTM_STATUS status;
   enh_esco_params_t params;
   if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pair) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate socket pair: %s", __func__,
+    LOG_ERROR("%s unable to allocate socket pair: %s", __func__,
               strerror(errno));
     goto error;
   }
 
   sco_socket = sco_socket_new();
   if (!sco_socket) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate new SCO socket.", __func__);
+    LOG_ERROR("%s unable to allocate new SCO socket.", __func__);
     goto error;
   }
 
@@ -154,14 +154,14 @@
                          &sco_socket->sco_handle, connect_completed_cb,
                          disconnect_completed_cb);
   if (status != BTM_CMD_STARTED) {
-    LOG_ERROR(LOG_TAG, "%s unable to create SCO socket: %d", __func__, status);
+    LOG_ERROR("%s unable to create SCO socket: %d", __func__, status);
     goto error;
   }
 
   socket = socket_new_from_fd(pair[1]);
   if (!socket) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate socket from file descriptor %d.",
-              __func__, pair[1]);
+    LOG_ERROR("%s unable to allocate socket from file descriptor %d.", __func__,
+              pair[1]);
     goto error;
   }
 
@@ -226,14 +226,14 @@
   sco_socket_t* new_sco_socket;
 
   if (!sco_socket) {
-    LOG_ERROR(LOG_TAG, "%s unable to find sco_socket for handle: %hu", __func__,
+    LOG_ERROR("%s unable to find sco_socket for handle: %hu", __func__,
               conn_data->sco_inx);
     goto error;
   }
 
   if (sco_socket != listen_sco_socket) {
     LOG_ERROR(
-        LOG_TAG,
+
         "%s received connection request on non-listening socket handle: %hu",
         __func__, conn_data->sco_inx);
     goto error;
@@ -241,7 +241,7 @@
 
   new_sco_socket = sco_socket_establish_locked(true, NULL, &client_fd);
   if (!new_sco_socket) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate new sco_socket.", __func__);
+    LOG_ERROR("%s unable to allocate new sco_socket.", __func__);
     goto error;
   }
 
@@ -259,8 +259,7 @@
   if (socket_write_and_transfer_fd(sco_socket->socket, &connect_signal,
                                    sizeof(connect_signal),
                                    client_fd) != sizeof(connect_signal)) {
-    LOG_ERROR(LOG_TAG,
-              "%s unable to send new file descriptor to listening socket.",
+    LOG_ERROR("%s unable to send new file descriptor to listening socket.",
               __func__);
     goto error;
   }
@@ -280,8 +279,8 @@
 
   sco_socket_t* sco_socket = sco_socket_find_locked(sco_handle);
   if (!sco_socket) {
-    LOG_ERROR(LOG_TAG, "%s SCO socket not found on connect for handle: %hu",
-              __func__, sco_handle);
+    LOG_ERROR("%s SCO socket not found on connect for handle: %hu", __func__,
+              sco_handle);
     return;
   }
 
@@ -302,8 +301,8 @@
 
   sco_socket_t* sco_socket = sco_socket_find_locked(sco_handle);
   if (!sco_socket) {
-    LOG_ERROR(LOG_TAG, "%s SCO socket not found on disconnect for handle: %hu",
-              __func__, sco_handle);
+    LOG_ERROR("%s SCO socket not found on disconnect for handle: %hu", __func__,
+              sco_handle);
     return;
   }
 
diff --git a/btif/src/btif_storage.cc b/btif/src/btif_storage.cc
index e203d2f..3503008 100644
--- a/btif/src/btif_storage.cc
+++ b/btif/src/btif_storage.cc
@@ -72,9 +72,6 @@
 #define BTIF_STORAGE_PATH_REMOTE_DEVCLASS "DevClass"
 #define BTIF_STORAGE_PATH_REMOTE_DEVTYPE "DevType"
 #define BTIF_STORAGE_PATH_REMOTE_NAME "Name"
-#define BTIF_STORAGE_PATH_REMOTE_VER_MFCT "Manufacturer"
-#define BTIF_STORAGE_PATH_REMOTE_VER_VER "LmpVer"
-#define BTIF_STORAGE_PATH_REMOTE_VER_SUBVER "LmpSubVer"
 
 //#define BTIF_STORAGE_PATH_REMOTE_LINKKEYS "remote_linkkeys"
 #define BTIF_STORAGE_PATH_REMOTE_ALIASE "Aliase"
@@ -85,6 +82,7 @@
 #define BTIF_STORAGE_KEY_LOCAL_IO_CAPS "LocalIOCaps"
 #define BTIF_STORAGE_KEY_LOCAL_IO_CAPS_BLE "LocalIOCapsBLE"
 #define BTIF_STORAGE_KEY_ADAPTER_DISC_TIMEOUT "DiscoveryTimeout"
+#define BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED "GattClientSupportedFeatures"
 
 /* This is a local property to add a device found */
 #define BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP 0xFF
@@ -260,11 +258,10 @@
 
       if (!info) return false;
 
-      btif_config_set_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_MFCT,
+      btif_config_set_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_MFCT,
                           info->manufacturer);
-      btif_config_set_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_VER,
-                          info->version);
-      btif_config_set_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_SUBVER,
+      btif_config_set_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_VER, info->version);
+      btif_config_set_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_SUBVER,
                           info->sub_ver);
     } break;
 
@@ -382,15 +379,15 @@
       bt_remote_version_t* info = (bt_remote_version_t*)prop->val;
 
       if (prop->len >= (int)sizeof(bt_remote_version_t)) {
-        ret = btif_config_get_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_MFCT,
+        ret = btif_config_get_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_MFCT,
                                   &info->manufacturer);
 
         if (ret)
-          ret = btif_config_get_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_VER,
+          ret = btif_config_get_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_VER,
                                     &info->version);
 
         if (ret)
-          ret = btif_config_get_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_SUBVER,
+          ret = btif_config_get_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_SUBVER,
                                     &info->sub_ver);
       }
     } break;
@@ -452,11 +449,8 @@
   bool bt_linkkey_file_found = false;
   int device_type;
 
-  // TODO: this code is not thread safe, it can corrupt config content.
-  // b/67595284
-  for (const section_t& section : btif_config_sections()) {
-    const std::string& name = section.name;
-    if (!RawAddress::IsValidAddress(name)) continue;
+  for (const auto& bd_addr : btif_config_get_paired_devices()) {
+    auto name = bd_addr.ToString();
 
     BTIF_TRACE_DEBUG("Remote device:%s", name.c_str());
     LinkKey link_key;
@@ -464,8 +458,6 @@
     if (btif_config_get_bin(name, "LinkKey", link_key.data(), &size)) {
       int linkkey_type;
       if (btif_config_get_int(name, "LinkKeyType", &linkkey_type)) {
-        RawAddress bd_addr;
-        RawAddress::FromString(name, bd_addr);
         if (add) {
           DEV_CLASS dev_class = {0, 0, 0};
           int cod;
@@ -505,7 +497,7 @@
   tBTA_LE_KEY_VALUE key;
   memset(&key, 0, sizeof(key));
 
-  if (btif_storage_get_ble_bonding_key(&bd_addr, key_type, (uint8_t*)&key,
+  if (btif_storage_get_ble_bonding_key(bd_addr, key_type, (uint8_t*)&key,
                                        key_len) == BT_STATUS_SUCCESS) {
     if (add_key) {
       if (!*device_added) {
@@ -636,13 +628,12 @@
     /* Fetch the local BD ADDR */
     const controller_t* controller = controller_get_interface();
     if (!controller->get_is_ready()) {
-      LOG_ERROR(LOG_TAG,
-                "%s: Controller not ready! Unable to return Bluetooth Address",
+      LOG_ERROR("%s: Controller not ready! Unable to return Bluetooth Address",
                 __func__);
       *bd_addr = RawAddress::kEmpty;
       return BT_STATUS_FAIL;
     } else {
-      LOG_ERROR(LOG_TAG, "%s: Controller ready!", __func__);
+      LOG_ERROR("%s: Controller ready!", __func__);
       *bd_addr = *controller->get_address();
     }
     property->len = RawAddress::kLength;
@@ -671,7 +662,7 @@
     uint32_t i;
 
     tBTA_SERVICE_MASK service_mask = btif_get_enabled_services_mask();
-    LOG_INFO(LOG_TAG, "%s service_mask:0x%x", __func__, service_mask);
+    LOG_INFO("%s service_mask:0x%x", __func__, service_mask);
     for (i = 0; i < BTA_MAX_SERVICE_ID; i++) {
       /* This should eventually become a function when more services are enabled
        */
@@ -863,6 +854,10 @@
   if (btif_config_exist(bdstr, BTIF_STORAGE_PATH_REMOTE_ALIASE)) {
     ret &= btif_config_remove(bdstr, BTIF_STORAGE_PATH_REMOTE_ALIASE);
   }
+  if (btif_config_exist(bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED)) {
+    ret &= btif_config_remove(bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED);
+  }
+
   /* write bonded info immediately */
   btif_config_flush();
   return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
@@ -877,20 +872,15 @@
  */
 static void remove_devices_with_sample_ltk() {
   std::vector<RawAddress> bad_ltk;
-  for (const section_t& section : btif_config_sections()) {
-    const std::string& name = section.name;
-    if (!RawAddress::IsValidAddress(name)) {
-      continue;
-    }
-
-    RawAddress bd_addr;
-    RawAddress::FromString(name, bd_addr);
+  for (const auto& bd_addr : btif_config_get_paired_devices()) {
+    auto name = bd_addr.ToString();
 
     tBTA_LE_KEY_VALUE key;
     memset(&key, 0, sizeof(key));
 
-    if (btif_storage_get_ble_bonding_key(&bd_addr, BTIF_DM_LE_KEY_PENC, (uint8_t*)&key, sizeof(tBTM_LE_PENC_KEYS)) ==
-        BT_STATUS_SUCCESS) {
+    if (btif_storage_get_ble_bonding_key(
+            bd_addr, BTIF_DM_LE_KEY_PENC, (uint8_t*)&key,
+            sizeof(tBTM_LE_PENC_KEYS)) == BT_STATUS_SUCCESS) {
       if (is_sample_ltk(key.penc_key.ltk)) {
         bad_ltk.push_back(bd_addr);
       }
@@ -1096,7 +1086,7 @@
  *                  BT_STATUS_FAIL otherwise
  *
  ******************************************************************************/
-bt_status_t btif_storage_get_ble_bonding_key(RawAddress* remote_bd_addr,
+bt_status_t btif_storage_get_ble_bonding_key(const RawAddress& remote_bd_addr,
                                              uint8_t key_type,
                                              uint8_t* key_value,
                                              int key_length) {
@@ -1125,7 +1115,7 @@
   }
   size_t length = key_length;
   int ret =
-      btif_config_get_bin(remote_bd_addr->ToString(), name, key_value, &length);
+      btif_config_get_bin(remote_bd_addr.ToString(), name, key_value, &length);
   return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
 }
 
@@ -1369,11 +1359,8 @@
  *
  ******************************************************************************/
 bt_status_t btif_storage_load_bonded_hid_info(void) {
-  // TODO: this code is not thread safe, it can corrupt config content.
-  // b/67595284
-  for (const section_t& section : btif_config_sections()) {
-    const std::string& name = section.name;
-    if (!RawAddress::IsValidAddress(name)) continue;
+  for (const auto& bd_addr : btif_config_get_paired_devices()) {
+    auto name = bd_addr.ToString();
 
     BTIF_TRACE_DEBUG("Remote device:%s", name.c_str());
 
@@ -1382,9 +1369,7 @@
     uint16_t attr_mask = (uint16_t)value;
 
     if (btif_in_fetch_bonded_device(name) != BT_STATUS_SUCCESS) {
-      RawAddress bd_addr;
-      RawAddress::FromString(name, bd_addr);
-      btif_storage_remove_hid_info(&bd_addr);
+      btif_storage_remove_hid_info(bd_addr);
       continue;
     }
 
@@ -1425,8 +1410,6 @@
                           (uint8_t*)dscp_info.descriptor.dsc_list, &len);
     }
 
-    RawAddress bd_addr;
-    RawAddress::FromString(name, bd_addr);
     // add extracted information to BTA HH
     if (btif_hh_add_added_dev(bd_addr, attr_mask)) {
       BTA_HhAddDev(bd_addr, attr_mask, sub_class, app_id, dscp_info);
@@ -1447,8 +1430,8 @@
  *                  BT_STATUS_FAIL otherwise
  *
  ******************************************************************************/
-bt_status_t btif_storage_remove_hid_info(RawAddress* remote_bd_addr) {
-  std::string bdstr = remote_bd_addr->ToString();
+bt_status_t btif_storage_remove_hid_info(const RawAddress& remote_bd_addr) {
+  std::string bdstr = remote_bd_addr.ToString();
 
   btif_config_remove(bdstr, "HidAttrMask");
   btif_config_remove(bdstr, "HidSubClass");
@@ -1517,11 +1500,8 @@
 
 /** Loads information about bonded hearing aid devices */
 void btif_storage_load_bonded_hearing_aids() {
-  // TODO: this code is not thread safe, it can corrupt config content.
-  // b/67595284
-  for (const section_t& section : btif_config_sections()) {
-    const std::string& name = section.name;
-    if (!RawAddress::IsValidAddress(name)) continue;
+  for (const auto& bd_addr : btif_config_get_paired_devices()) {
+    const std::string& name = bd_addr.ToString();
 
     int size = STORAGE_UUID_STRING_SIZE * HEARINGAID_MAX_NUM_UUIDS;
     char uuid_str[size];
@@ -1545,8 +1525,6 @@
     BTIF_TRACE_DEBUG("Remote device:%s", name.c_str());
 
     if (btif_in_fetch_bonded_device(name) != BT_STATUS_SUCCESS) {
-      RawAddress bd_addr;
-      RawAddress::FromString(name, bd_addr);
       btif_storage_remove_hearing_aid(bd_addr);
       continue;
     }
@@ -1601,9 +1579,6 @@
     if (btif_config_get_int(name, HEARING_AID_IS_WHITE_LISTED, &value))
       is_white_listed = value;
 
-    RawAddress bd_addr;
-    RawAddress::FromString(name, bd_addr);
-
     // add extracted information to BTA Hearing Aid
     do_in_main_thread(
         FROM_HERE,
@@ -1715,18 +1690,13 @@
  *
  ******************************************************************************/
 bt_status_t btif_storage_load_hidd(void) {
-  // TODO: this code is not thread safe, it can corrupt config content.
-  // b/67595284
-  for (const section_t& section : btif_config_sections()) {
-    const std::string& name = section.name;
-    if (!RawAddress::IsValidAddress(name)) continue;
+  for (const auto& bd_addr : btif_config_get_paired_devices()) {
+    auto name = bd_addr.ToString();
 
     BTIF_TRACE_DEBUG("Remote device:%s", name.c_str());
     int value;
     if (btif_in_fetch_bonded_device(name) == BT_STATUS_SUCCESS) {
       if (btif_config_get_int(name, "HidDeviceCabled", &value)) {
-        RawAddress bd_addr;
-        RawAddress::FromString(name, bd_addr);
         BTA_HdAddDevice(bd_addr);
         break;
       }
@@ -1746,13 +1716,13 @@
  * Returns          BT_STATUS_SUCCESS
  *
  ******************************************************************************/
-bt_status_t btif_storage_set_hidd(RawAddress* remote_bd_addr) {
-  std::string remote_device_address_string = remote_bd_addr->ToString();
-  for (const section_t& section : btif_config_sections()) {
-    if (!RawAddress::IsValidAddress(section.name)) continue;
-    if (section.name == remote_device_address_string) continue;
-    if (btif_in_fetch_bonded_device(section.name) == BT_STATUS_SUCCESS) {
-      btif_config_remove(section.name, "HidDeviceCabled");
+bt_status_t btif_storage_set_hidd(const RawAddress& remote_bd_addr) {
+  std::string remote_device_address_string = remote_bd_addr.ToString();
+  for (const auto& bd_addr : btif_config_get_paired_devices()) {
+    auto name = bd_addr.ToString();
+    if (bd_addr == remote_bd_addr) continue;
+    if (btif_in_fetch_bonded_device(name) == BT_STATUS_SUCCESS) {
+      btif_config_remove(name, "HidDeviceCabled");
     }
   }
 
@@ -1788,3 +1758,31 @@
   return (btif_storage_get_remote_device_property(&bd_addr, &property) ==
           BT_STATUS_SUCCESS);
 }
+
+/** Stores information about GATT Client supported features support */
+void btif_storage_set_gatt_cl_supp_feat(const RawAddress& bd_addr,
+                                        uint8_t feat) {
+  do_in_jni_thread(
+      FROM_HERE, Bind(
+                     [](const RawAddress& bd_addr, uint8_t feat) {
+                       std::string bdstr = bd_addr.ToString();
+                       VLOG(2)
+                           << "saving gatt client supported feat: " << bdstr;
+                       btif_config_set_int(
+                           bdstr, BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED, feat);
+                       btif_config_save();
+                     },
+                     bd_addr, feat));
+}
+
+/** Get client supported features */
+uint8_t btif_storage_get_gatt_cl_supp_feat(const RawAddress& bd_addr) {
+  auto name = bd_addr.ToString();
+
+  int value = 0;
+  btif_config_get_int(name, BTIF_STORAGE_KEY_GATT_CLIENT_SUPPORTED, &value);
+  BTIF_TRACE_DEBUG("Remote device: %s GATT client supported features 0x%02x",
+                   name.c_str(), value);
+
+  return value;
+}
diff --git a/btif/src/btif_util.cc b/btif/src/btif_util.cc
index 26d84fd..0aa1142 100644
--- a/btif/src/btif_util.cc
+++ b/btif/src/btif_util.cc
@@ -357,6 +357,7 @@
     CASE_RETURN_STR(BTA_AV_VENDOR_RSP_EVT)
     CASE_RETURN_STR(BTA_AV_META_MSG_EVT)
     CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT)
+    CASE_RETURN_STR(BTA_AV_RC_PSM_EVT)
     default:
       return "UNKNOWN_EVENT";
   }
diff --git a/btif/src/stack_manager.cc b/btif/src/stack_manager.cc
index 60c2c0c..736c5cc 100644
--- a/btif/src/stack_manager.cc
+++ b/btif/src/stack_manager.cc
@@ -28,6 +28,7 @@
 #include "btif_common.h"
 #include "common/message_loop_thread.h"
 #include "device/include/controller.h"
+#include "main/shim/shim.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 #include "osi/include/semaphore.h"
@@ -103,16 +104,18 @@
 static void event_init_stack(void* context) {
   semaphore_t* semaphore = (semaphore_t*)context;
 
-  LOG_INFO(LOG_TAG, "%s is initializing the stack", __func__);
+  LOG_INFO("%s is initializing the stack", __func__);
 
   if (stack_is_initialized) {
-    LOG_INFO(LOG_TAG, "%s found the stack already in initialized state",
-             __func__);
+    LOG_INFO("%s found the stack already in initialized state", __func__);
   } else {
     module_management_start();
 
     module_init(get_module(OSI_MODULE));
     module_init(get_module(BT_UTILS_MODULE));
+    if (bluetooth::shim::is_gd_shim_enabled()) {
+      module_start_up(get_module(GD_IDLE_MODULE));
+    }
     module_init(get_module(BTIF_CONFIG_MODULE));
     btif_init_bluetooth();
 
@@ -120,14 +123,14 @@
     stack_is_initialized = true;
   }
 
-  LOG_INFO(LOG_TAG, "%s finished", __func__);
+  LOG_INFO("%s finished", __func__);
 
   if (semaphore) semaphore_post(semaphore);
 }
 
 static void ensure_stack_is_initialized() {
   if (!stack_is_initialized) {
-    LOG_WARN(LOG_TAG, "%s found the stack was uninitialized. Initializing now.",
+    LOG_WARN("%s found the stack was uninitialized. Initializing now.",
              __func__);
     // No semaphore needed since we are calling it directly
     event_init_stack(nullptr);
@@ -137,40 +140,39 @@
 // Synchronous function to start up the stack
 static void event_start_up_stack(UNUSED_ATTR void* context) {
   if (stack_is_running) {
-    LOG_INFO(LOG_TAG, "%s stack already brought up", __func__);
+    LOG_INFO("%s stack already brought up", __func__);
     return;
   }
 
   ensure_stack_is_initialized();
 
-  LOG_INFO(LOG_TAG, "%s is bringing up the stack", __func__);
+  LOG_INFO("%s is bringing up the stack", __func__);
   future_t* local_hack_future = future_new();
   hack_future = local_hack_future;
 
   // Include this for now to put btif config into a shutdown-able state
-  module_start_up(get_module(BTIF_CONFIG_MODULE));
   bte_main_enable();
 
   if (future_await(local_hack_future) != FUTURE_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s failed to start up the stack", __func__);
+    LOG_ERROR("%s failed to start up the stack", __func__);
     stack_is_running = true;  // So stack shutdown actually happens
     event_shut_down_stack(nullptr);
     return;
   }
 
   stack_is_running = true;
-  LOG_INFO(LOG_TAG, "%s finished", __func__);
+  LOG_INFO("%s finished", __func__);
   do_in_jni_thread(FROM_HERE, base::Bind(event_signal_stack_up, nullptr));
 }
 
 // Synchronous function to shut down the stack
 static void event_shut_down_stack(UNUSED_ATTR void* context) {
   if (!stack_is_running) {
-    LOG_INFO(LOG_TAG, "%s stack is already brought down", __func__);
+    LOG_INFO("%s stack is already brought down", __func__);
     return;
   }
 
-  LOG_INFO(LOG_TAG, "%s is bringing down the stack", __func__);
+  LOG_INFO("%s is bringing down the stack", __func__);
   future_t* local_hack_future = future_new();
   hack_future = local_hack_future;
   stack_is_running = false;
@@ -186,13 +188,12 @@
   hack_future = future_new();
   do_in_jni_thread(FROM_HERE, base::Bind(event_signal_stack_down, nullptr));
   future_await(hack_future);
-  LOG_INFO(LOG_TAG, "%s finished", __func__);
+  LOG_INFO("%s finished", __func__);
 }
 
 static void ensure_stack_is_not_running() {
   if (stack_is_running) {
-    LOG_WARN(LOG_TAG,
-             "%s found the stack was still running. Bringing it down now.",
+    LOG_WARN("%s found the stack was still running. Bringing it down now.",
              __func__);
     event_shut_down_stack(nullptr);
   }
@@ -201,21 +202,22 @@
 // Synchronous function to clean up the stack
 static void event_clean_up_stack(void* context) {
   if (!stack_is_initialized) {
-    LOG_INFO(LOG_TAG, "%s found the stack already in a clean state", __func__);
+    LOG_INFO("%s found the stack already in a clean state", __func__);
     goto cleanup;
   }
 
   ensure_stack_is_not_running();
 
-  LOG_INFO(LOG_TAG, "%s is cleaning up the stack", __func__);
+  LOG_INFO("%s is cleaning up the stack", __func__);
   stack_is_initialized = false;
 
   btif_cleanup_bluetooth();
   module_clean_up(get_module(BTIF_CONFIG_MODULE));
   module_clean_up(get_module(BT_UTILS_MODULE));
   module_clean_up(get_module(OSI_MODULE));
+  module_shut_down(get_module(GD_IDLE_MODULE));
   module_management_stop();
-  LOG_INFO(LOG_TAG, "%s finished", __func__);
+  LOG_INFO("%s finished", __func__);
 
 cleanup:;
   semaphore_t* semaphore = (semaphore_t*)context;
@@ -239,7 +241,7 @@
 
   management_thread.StartUp();
   if (!management_thread.IsRunning()) {
-    LOG_ERROR(LOG_TAG, "%s unable to start stack management thread", __func__);
+    LOG_ERROR("%s unable to start stack management thread", __func__);
     return;
   }
 }
diff --git a/btif/test/btif_config_cache_test.cc b/btif/test/btif_config_cache_test.cc
new file mode 100644
index 0000000..b7c0e9b
--- /dev/null
+++ b/btif/test/btif_config_cache_test.cc
@@ -0,0 +1,528 @@
+/*
+ *  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.
+ */
+
+#include "btif/include/btif_config_cache.h"
+
+#include <filesystem>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+const int kCapacity = 3;
+const int kTestRepeatCount = 30;
+const std::string kBtAddr1 = "11:22:33:44:55:66";
+const std::string kBtAddr2 = "AA:BB:CC:DD:EE:FF";
+const std::string kBtAddr3 = "AB:CD:EF:12:34:56";
+const std::string kBtAddr4 = "11:AA:22:BB:33:CC";
+const std::string kBtAddr5 = "11:AA:22:BB:33:CD";
+const std::string kBtLocalAddr = "12:34:56:78:90:AB";
+const std::string kBtInfo = "Info";
+const std::string kBtMetrics = "Metrics";
+const std::string kBtAdapter = "Adapter";
+const std::string kBtAddrInvalid1 = "AB:CD:EF:12:34";
+const std::string kBtAddrInvalid2 = "AB:CD:EF:12:34:56:78";
+const std::string kBtAddrInvalid3 = "ABCDEF123456";
+const std::string kBtAddrInvalid4 = "AB-CD-EF-12-34-56";
+const std::string kBtSectionInvalid1 = "Invalid Section";
+const std::filesystem::path kTestConfigFile =
+    std::filesystem::temp_directory_path() / "config_cache_test.conf";
+const char* TEST_CONFIG_FILE = kTestConfigFile.c_str();
+
+}  // namespace
+
+namespace testing {
+
+/* Test to basic btif_config_cache set up
+ * 1. when received Local device sections information, the sections can be put
+ * into btif config cache
+ * 2. the device sections and key-value will be set to Btif config cache when
+ * receiving different device sections
+ * 3. limit the capacity of unpacire devices cache to 3, test the oldest device
+ * section will be ruled out when receiveing 4 different device sections.
+ */
+TEST(BtifConfigCacheTest, test_setup_btif_config_cache) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // Info section
+  test_btif_config_cache.SetString(kBtInfo, "FileSource", "");
+  test_btif_config_cache.SetString(kBtInfo, "TimeCreated",
+                                   "2020-06-05 12:12:12");
+  // Metrics section
+  test_btif_config_cache.SetString(kBtMetrics, "Salt256Bit",
+                                   "92a331174d20f2bb");
+  // Adapter Section
+  test_btif_config_cache.SetString(kBtAdapter, "Address", kBtLocalAddr);
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAdapter));
+
+  // bt_device_1
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 1);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Int"));
+
+  // bt_device_2
+  test_btif_config_cache.SetString(kBtAddr2, "Name", "Headset_2");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr2, "Name"));
+
+  // bt_device_3
+  test_btif_config_cache.SetString(kBtAddr3, "Name", "Headset_3");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr3, "Name"));
+
+  // bt_device_4
+  test_btif_config_cache.SetString(kBtAddr4, "Name", "Headset_4");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr4, "Name"));
+
+  // out out the capacty of unpair devices cache, the bt_device_1 be ruled out
+  EXPECT_FALSE(test_btif_config_cache.HasSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr2));
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr3));
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr4));
+}
+
+/* Test to set up btif_config_cache with invalid bt address or section name
+ * when received Invalid bt address or section, it's not allowed to put invalid
+ * section to paired devices list section
+ */
+TEST(BtifConfigCacheTest, test_set_up_config_cache_with_invalid_section) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+
+  // kBtAddrInvalid1
+  test_btif_config_cache.SetString(kBtAddrInvalid1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid1, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid1));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddrInvalid1, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid1, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid1));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid1));
+
+  // kBtAddrInvalid2
+  test_btif_config_cache.SetString(kBtAddrInvalid2, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid2, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid2));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddrInvalid2, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid2, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid2));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid2));
+
+  // kBtAddrInvalid3
+  test_btif_config_cache.SetString(kBtAddrInvalid3, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid3, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid3));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddrInvalid3, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid3, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid3));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid3));
+
+  // kBtAddrInvalid4
+  test_btif_config_cache.SetString(kBtAddrInvalid4, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid4, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid4));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddrInvalid4, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid4, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid4));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid4));
+
+  // kBtSectionInvalid1
+  test_btif_config_cache.SetString(kBtSectionInvalid1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtSectionInvalid1, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtSectionInvalid1));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtSectionInvalid1, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtSectionInvalid1, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtSectionInvalid1));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtSectionInvalid1));
+}
+
+/* Stress test to set and get key values
+ * 1. stress test to set different type key value to unpaired device cache and
+ * get the different type key values in the unpaired cache section to check if
+ * we get the key-values the same with we set in unpaired device cache.
+ * 2. stress test to set different type key value to paired device section and
+ * get the different type key values in the paired cache section to check if we
+ * get the key-values the same with we set in paired device cache.
+ */
+TEST(BtifConfigCacheTest, test_get_set_key_value_test) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // test in unpaired cache
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1")));
+
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 65536);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Int"));
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(65536)));
+
+  test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 4294967296);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_64"));
+  EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"),
+              Optional(Eq(uint64_t(4294967296))));
+
+  test_btif_config_cache.SetBool(kBtAddr1, "Property_Bool", true);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Bool"));
+  EXPECT_THAT(test_btif_config_cache.GetBool(kBtAddr1, "Property_Bool"),
+              Optional(IsTrue()));
+
+  // empty value
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("")));
+
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+  EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+
+  // test in unpaired cache
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1")));
+
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 65536);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Int"));
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(65536)));
+
+  test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 4294967296);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_64"));
+  EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"),
+              Optional(Eq(uint64_t(4294967296))));
+
+  test_btif_config_cache.SetBool(kBtAddr1, "Property_Bool", true);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Bool"));
+  EXPECT_THAT(test_btif_config_cache.GetBool(kBtAddr1, "Property_Bool"),
+              Optional(IsTrue()));
+
+  // empty section is disallowed
+  EXPECT_DEATH({ test_btif_config_cache.SetString("", "name", "Headset_1"); },
+               "Empty section not allowed");
+  // empty key is disallowed
+  EXPECT_DEATH({ test_btif_config_cache.SetString(kBtAddr1, "", "Headset_1"); },
+               "Empty key not allowed");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr1, ""));
+  // empty value is allowed
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("")));
+}
+
+/* Test to set values in the same key
+ * Receiving the same key with different values in a section, the new incoming
+ * value will be updated but the key will not be added repeatedly. test this
+ * feature in both unpaired devic cache and paired device list cache
+ */
+TEST(BtifConfigCacheTest, test_set_values_in_the_same_key) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // add new a key "Name"
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1")));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+
+  // add the same key "Name" with different value
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1A");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1A")));
+
+  // add the same key "Name" with different value
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_2A");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_2A")));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+
+  // add new a key "Property_Int"
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 65536);
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(65536)));
+
+  // add the same key "Property_Int" with different value
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 256);
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(256)));
+
+  test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 4294967296);
+  EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"),
+              Optional(Eq(uint64_t(4294967296))));
+
+  // get the LinkKey and set values in the same key in paired device list
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+  EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+
+  // add the same key "Name" with the different value
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1A");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1A")));
+
+  // add the same key "Name" with the value different
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_2A");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_2A")));
+
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 64);
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(64)));
+
+  test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 65537);
+  EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"),
+              Optional(Eq(uint64_t(65537))));
+
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+}
+
+/* Stress test to pair with device then unpair device
+ * 1. paired with device by adding a "LinKey" to device and check the device be
+ * moved into paired devices list
+ * 2. unpaired with the device by removing the "LinkKey" and check the device be
+ * moved back to unpaired devices cache
+ * 3. loop for 30 times
+ */
+TEST(BtifConfigCacheTest, test_pair_unpair_device_stress_test) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+
+  // pair with Headset_1 11:22:33:44:55:66
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+
+  for (int i = 0; i < kTestRepeatCount; ++i) {
+    // get the LinkKey, the device will be moved from the unpaired cache to
+    // paired cache
+    test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+    EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+    EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+
+    // remove the LinkKey, the device will be moved from the paired cache to
+    // unpaired cache
+    test_btif_config_cache.RemoveKey(kBtAddr1, "LinkKey");
+    EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+    EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+    EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+  }
+}
+
+/* Stress test to pair with multi-devices and unpair with multi-devices
+ * 1. Pired with 4 devices with Link-Key type key in order, to check these 4
+ * devices are in the paired devices list cache
+ * 2. unpair with these 4 devices by removed Link-Key type key in order, to
+ * check the fisrt device was ruled-out from unpaired devices cache due to
+ * capacity limitation, and other 3 devices are be moved to unpaired device
+ * cache.
+ */
+TEST(BtifConfigCacheTest, test_multi_pair_unpair_with_devices) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // pair with 4 bt address devices by add different type linkkey.
+  test_btif_config_cache.SetString(kBtAddr1, "name", "kBtAddr1");
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+
+  test_btif_config_cache.SetString(kBtAddr2, "name", "kBtAddr2");
+  test_btif_config_cache.SetString(kBtAddr2, "LE_KEY_PENC", "aabbccddeeff9900");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr2, "LE_KEY_PENC"));
+
+  test_btif_config_cache.SetString(kBtAddr3, "name", "kBtAddr3");
+  test_btif_config_cache.SetString(kBtAddr3, "LE_KEY_PID", "a1b2c3d4e5feeeee");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr3, "LE_KEY_PID"));
+
+  test_btif_config_cache.SetString(kBtAddr4, "LE_KEY_PCSRK",
+                                   "aaaabbbbccccdddd");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr4, "LE_KEY_PCSRK"));
+
+  test_btif_config_cache.SetString(kBtAddr5, "name", "kBtAddr5");
+  test_btif_config_cache.SetString(kBtAddr5, "LE_KEY_LENC", "jilkjlkjlkn");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr5, "LE_KEY_LENC"));
+
+  // checking these 4 devices are in paired list cache and the content are
+  // correct.
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "LinkKey"),
+              Optional(StrEq("1122334455667788")));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr2));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr2, "LE_KEY_PENC"),
+              Optional(StrEq("aabbccddeeff9900")));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr3));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr3, "LE_KEY_PID"),
+              Optional(StrEq("a1b2c3d4e5feeeee")));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr4));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr4, "LE_KEY_PCSRK"),
+              Optional(StrEq("aaaabbbbccccdddd")));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr5));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr5, "LE_KEY_LENC"),
+              Optional(StrEq("jilkjlkjlkn")));
+
+  // unpair with these 4 bt address devices by removed the linkkey.
+  // unpair kBtAddr1 11:22:33:44:55:66
+  test_btif_config_cache.RemoveKey(kBtAddr1, "LinkKey");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+  // no empty section is moved to unpaired
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "name"),
+              Optional(StrEq("kBtAddr1")));
+
+  // unpair with kBtAddr2 aa:bb:cc:dd:ee:ff
+  test_btif_config_cache.RemoveKey(kBtAddr2, "LE_KEY_PENC");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr2, "LE_KEY_PENC"));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr2));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr2));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr2, "name"),
+              Optional(StrEq("kBtAddr2")));
+
+  // unpair with kBtAddr3 AB:CD:EF:12:34:56
+  test_btif_config_cache.RemoveKey(kBtAddr3, "LE_KEY_PID");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr3, "LE_KEY_PID"));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr3));
+  // no empty section is moved to unpaired
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr3));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr3, "name"),
+              Optional(StrEq("kBtAddr3")));
+
+  // unpair with kBtAddr4 11:AA:22:BB:33:CC
+  test_btif_config_cache.RemoveKey(kBtAddr4, "LE_KEY_PCSRK");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr4, "LE_KEY_PCSRK"));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr4));
+  // empty section is removed
+  EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr4));
+
+  // unpair with kBtAddr5 11:AA:22:BB:33:CD
+  test_btif_config_cache.RemoveKey(kBtAddr5, "LE_KEY_LENC");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr5, "LE_KEY_LENC"));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr5));
+  // no empty section is moved to unpaired
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr5));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr5, "name"),
+              Optional(StrEq("kBtAddr5")));
+
+  // checking the oldest unpaired device kBtAddr1 was ruled out from cache due
+  // to capacity limitation (3) in unpaired cache.
+  EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+}
+
+/* Test to remove sections with the specific key
+ * paired with sections with the specific "Restricted" key and then removed the
+ * "Restricted" key, check if the sections with the specific "Restricted" key
+ * are removed.
+ */
+TEST(BtifConfigCacheTest, test_remove_sections_with_key) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // pair with Headset_1 (kBtAddr1), Headset_2 (kBtAddr1), Heasdet_3 (kBtAddr3)
+  // , and Headset_1 (kBtAddr1), Headset_3 (kBtAddr3) have sepcific "Restricted"
+  // key
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  test_btif_config_cache.SetString(kBtAddr1, "Restricted", "1");
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  test_btif_config_cache.SetString(kBtAddr2, "Name", "Headset_2");
+  test_btif_config_cache.SetString(kBtAddr2, "LinkKey", "aabbccddeeff9900");
+  test_btif_config_cache.SetString(kBtAddr3, "Name", "Headset_3");
+  test_btif_config_cache.SetString(kBtAddr3, "LinkKey", "a1b2c3d4e5feeeee");
+  test_btif_config_cache.SetString(kBtAddr3, "Restricted", "1");
+
+  // remove sections with "Restricted" key
+  test_btif_config_cache.RemovePersistentSectionsWithKey("Restricted");
+
+  // checking the kBtAddr1 and kBtAddr3 can not be found in config cache, only
+  // keep kBtAddr2 in config cache.
+  EXPECT_FALSE(test_btif_config_cache.HasSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr2));
+  EXPECT_FALSE(test_btif_config_cache.HasSection(kBtAddr3));
+}
+
+/* Test PersistentSectionCopy and Init */
+TEST(BtifConfigCacheTest, test_PersistentSectionCopy_Init) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  config_t config_paired = {};
+  // pair with 3 bt devices, kBtAddr1, kBtAddr2, kBtAddr3
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "LinkKey"),
+              Optional(StrEq("1122334455667788")));
+
+  test_btif_config_cache.SetString(kBtAddr2, "LE_KEY_PENC", "aabbccddeeff9900");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr2, "LE_KEY_PENC"));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr2));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr2, "LE_KEY_PENC"),
+              Optional(StrEq("aabbccddeeff9900")));
+
+  test_btif_config_cache.SetString(kBtAddr3, "LE_KEY_PID", "a1b2c3d4e5feeeee");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr3, "LE_KEY_PID"));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr3));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr3, "LE_KEY_PID"),
+              Optional(StrEq("a1b2c3d4e5feeeee")));
+
+  // check GetPersistentSections
+  int num_of_paired_devices = 0;
+  for (const auto& sec_name :
+       test_btif_config_cache.GetPersistentSectionNames()) {
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec_name));
+    num_of_paired_devices++;
+  }
+  EXPECT_EQ(num_of_paired_devices, 3);
+
+  // copy persistent sections
+  int num_of_copy_paired_devices = 0;
+  config_paired = test_btif_config_cache.PersistentSectionCopy();
+  for (const section_t& sec : config_paired.sections) {
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name));
+    num_of_copy_paired_devices++;
+  }
+  EXPECT_EQ(num_of_copy_paired_devices, 3);
+
+  // write persistent sections to temp test config file
+  EXPECT_TRUE(config_save(config_paired, TEST_CONFIG_FILE));
+  // get persistent sections from temp test config file
+  int num_of_save_paired_devices = 0;
+  std::unique_ptr<config_t> config_source = config_new(TEST_CONFIG_FILE);
+  for (const section_t& sec : config_paired.sections) {
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name));
+    num_of_save_paired_devices++;
+  }
+  EXPECT_EQ(num_of_save_paired_devices, 3);
+
+  // Clear all btif config cache sections
+  test_btif_config_cache.Clear();
+
+  // move the persistent sections to btif config paired list
+  int num_of_init_paired_devices = 0;
+  test_btif_config_cache.Init(std::move(config_source));
+  for (const section_t& sec : config_paired.sections) {
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name));
+    num_of_init_paired_devices++;
+  }
+  EXPECT_EQ(num_of_init_paired_devices, 3);
+
+  EXPECT_TRUE(std::filesystem::remove(kTestConfigFile));
+}
+
+}  // namespace testing
diff --git a/btif/test/btif_hf_client_service_test.cc b/btif/test/btif_hf_client_service_test.cc
new file mode 100755
index 0000000..929524e
--- /dev/null
+++ b/btif/test/btif_hf_client_service_test.cc
@@ -0,0 +1,47 @@
+#include <base/logging.h>
+#include <gtest/gtest.h>
+
+#undef LOG_TAG
+#include "btif/src/btif_hf_client.cc"
+
+static tBTA_HF_CLIENT_FEAT gFeatures;
+
+
+uint8_t btif_trace_level = BT_TRACE_LEVEL_WARNING;
+void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
+tBTA_STATUS BTA_HfClientEnable(tBTA_HF_CLIENT_CBACK* p_cback, tBTA_SEC sec_mask,
+                               tBTA_HF_CLIENT_FEAT features,
+                               const char* p_service_name) {
+  gFeatures = features;
+  return BTA_SUCCESS;
+}
+void BTA_HfClientDisable(void) { }
+bt_status_t btif_transfer_context(tBTIF_CBACK* p_cback, uint16_t event,
+                                  char* p_params, int param_len,
+                                  tBTIF_COPY_CBACK* p_copy_cback) {
+  return BT_STATUS_SUCCESS;
+}
+void btif_queue_advance() {}
+const char* dump_hf_client_event(uint16_t event) {
+  return "UNKNOWN MSG ID";
+}
+
+class BtifHfClientTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    gFeatures = BTIF_HF_CLIENT_FEATURES;
+  }
+
+  void TearDown() override {}
+};
+
+TEST_F(BtifHfClientTest, test_btif_hf_cleint_service) {
+  bool enable = true;
+
+  osi_property_set("persist.bluetooth.hfpclient.sco_s4_supported", "true");
+  btif_hf_client_execute_service(enable);
+  EXPECT_TRUE(gFeatures & BTA_HF_CLIENT_FEAT_S4);
+  osi_property_set("persist.bluetooth.hfpclient.sco_s4_supported", "false");
+  btif_hf_client_execute_service(enable);
+  EXPECT_FALSE(gFeatures & BTA_HF_CLIENT_FEAT_S4);
+}
diff --git a/btif/test/btif_rc_test.cc b/btif/test/btif_rc_test.cc
new file mode 100644
index 0000000..6271881
--- /dev/null
+++ b/btif/test/btif_rc_test.cc
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <cstdint>
+
+#include "bta/include/bta_av_api.h"
+#include "btif/include/btif_common.h"
+#include "device/include/interop.h"
+#include "include/hardware/bt_rc.h"
+#include "osi/test/AllocationTestHarness.h"
+#include "stack/include/btm_api_types.h"
+#include "types/raw_address.h"
+#undef LOG_TAG
+#include "btif/src/btif_rc.cc"
+
+extern void allocation_tracker_uninit(void);
+
+namespace {
+int AVRC_BldResponse_ = 0;
+}  // namespace
+
+uint8_t appl_trace_level = BT_TRACE_LEVEL_WARNING;
+uint8_t btif_trace_level = BT_TRACE_LEVEL_WARNING;
+
+tAVRC_STS AVRC_BldCommand(tAVRC_COMMAND* p_cmd, BT_HDR** pp_pkt) { return 0; }
+tAVRC_STS AVRC_BldResponse(uint8_t handle, tAVRC_RESPONSE* p_rsp,
+                           BT_HDR** pp_pkt) {
+  AVRC_BldResponse_++;
+  return 0;
+}
+tAVRC_STS AVRC_Ctrl_ParsCommand(tAVRC_MSG* p_msg, tAVRC_COMMAND* p_result) {
+  return 0;
+}
+tAVRC_STS AVRC_Ctrl_ParsResponse(tAVRC_MSG* p_msg, tAVRC_RESPONSE* p_result,
+                                 uint8_t* p_buf, uint16_t* buf_len) {
+  return 0;
+}
+tAVRC_STS AVRC_ParsCommand(tAVRC_MSG* p_msg, tAVRC_COMMAND* p_result,
+                           uint8_t* p_buf, uint16_t buf_len) {
+  return 0;
+}
+tAVRC_STS AVRC_ParsResponse(tAVRC_MSG* p_msg, tAVRC_RESPONSE* p_result,
+                            UNUSED_ATTR uint8_t* p_buf,
+                            UNUSED_ATTR uint16_t buf_len) {
+  return 0;
+}
+void BTA_AvCloseRc(uint8_t rc_handle) {}
+void BTA_AvMetaCmd(uint8_t rc_handle, uint8_t label, tBTA_AV_CMD cmd_code,
+                   BT_HDR* p_pkt) {}
+void BTA_AvMetaRsp(uint8_t rc_handle, uint8_t label, tBTA_AV_CODE rsp_code,
+                   BT_HDR* p_pkt) {}
+void BTA_AvRemoteCmd(uint8_t rc_handle, uint8_t label, tBTA_AV_RC rc_id,
+                     tBTA_AV_STATE key_state) {}
+void BTA_AvRemoteVendorUniqueCmd(uint8_t rc_handle, uint8_t label,
+                                 tBTA_AV_STATE key_state, uint8_t* p_msg,
+                                 uint8_t buf_len) {}
+void BTA_AvVendorCmd(uint8_t rc_handle, uint8_t label, tBTA_AV_CODE cmd_code,
+                     uint8_t* p_data, uint16_t len) {}
+void BTA_AvVendorRsp(uint8_t rc_handle, uint8_t label, tBTA_AV_CODE rsp_code,
+                     uint8_t* p_data, uint16_t len, uint32_t company_id) {}
+void btif_av_clear_remote_suspend_flag(void) {}
+bool btif_av_is_connected(void) { return false; }
+bool btif_av_is_sink_enabled(void) { return false; }
+RawAddress btif_av_sink_active_peer(void) { return RawAddress(); }
+RawAddress btif_av_source_active_peer(void) { return RawAddress(); }
+bool btif_av_stream_started_ready(void) { return false; }
+bt_status_t btif_transfer_context(tBTIF_CBACK* p_cback, uint16_t event,
+                                  char* p_params, int param_len,
+                                  tBTIF_COPY_CBACK* p_copy_cback) {
+  return BT_STATUS_SUCCESS;
+}
+const char* dump_rc_event(uint8_t event) { return nullptr; }
+const char* dump_rc_notification_event_id(uint8_t event_id) { return nullptr; }
+const char* dump_rc_pdu(uint8_t pdu) { return nullptr; }
+bt_status_t do_in_jni_thread(const base::Location& from_here,
+                             base::OnceClosure task) {
+  return BT_STATUS_SUCCESS;
+}
+base::MessageLoop* get_main_message_loop() { return nullptr; }
+bool interop_match_addr(const interop_feature_t feature,
+                        const RawAddress* addr) {
+  return false;
+}
+void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
+
+/**
+ * Test class to test selected functionality in hci/src/hci_layer.cc
+ */
+class BtifRcTest : public AllocationTestHarness {
+ protected:
+  void SetUp() override {
+    AllocationTestHarness::SetUp();
+    // Disable our allocation tracker to allow ASAN full range
+    allocation_tracker_uninit();
+  }
+
+  void TearDown() override { AllocationTestHarness::TearDown(); }
+};
+
+TEST_F(BtifRcTest, get_element_attr_rsp) {
+  RawAddress bd_addr;
+
+  btif_rc_cb.rc_multi_cb[0].rc_addr = bd_addr;
+  btif_rc_cb.rc_multi_cb[0].rc_connected = true;
+  btif_rc_cb.rc_multi_cb[0]
+      .rc_pdu_info[IDX_GET_ELEMENT_ATTR_RSP]
+      .is_rsp_pending = true;
+  btif_rc_cb.rc_multi_cb[0].rc_state = BTRC_CONNECTION_STATE_CONNECTED;
+
+  btrc_element_attr_val_t p_attrs[BTRC_MAX_ELEM_ATTR_SIZE];
+  uint8_t num_attr = BTRC_MAX_ELEM_ATTR_SIZE + 1;
+
+  CHECK(get_element_attr_rsp(bd_addr, num_attr, p_attrs) == BT_STATUS_SUCCESS);
+  CHECK(AVRC_BldResponse_ == 1);
+}
diff --git a/build/fluoride.go b/build/fluoride.go
index 6a511a5..4ace71b 100644
--- a/build/fluoride.go
+++ b/build/fluoride.go
@@ -43,7 +43,7 @@
 	ctx.AppendProperties(p)
 }
 
-func globalDefaults(ctx android.BaseContext) ([]string, []string) {
+func globalDefaults(ctx android.LoadHookContext) ([]string, []string) {
 	var cflags []string
 	var includeDirs []string
 
diff --git a/build/toolchain/clang/get_clang_suffix.py b/build/toolchain/clang/get_clang_suffix.py
index dedacdd..6bf7b9c 100644
--- a/build/toolchain/clang/get_clang_suffix.py
+++ b/build/toolchain/clang/get_clang_suffix.py
@@ -3,41 +3,42 @@
 import re
 import sys
 
-def which(cmd):
-  for p in os.environ["PATH"].split(os.pathsep):
-    clang_path = os.path.join(p, cmd)
-    if os.path.exists(clang_path):
-      return clang_path
-  return None
 
-CLANG_VERSION_REGEX=".*version\s*([0-9]*\.[0-9]*)\.*"
+def which(cmd):
+    for p in os.environ["PATH"].split(os.pathsep):
+        clang_path = os.path.join(p, cmd)
+        if os.path.exists(clang_path):
+            return clang_path
+    return None
+
+
+CLANG_VERSION_REGEX = ".*version\s*([0-9]*\.[0-9]*)\.*"
 clang_path = which("clang++")
 clang_version_major = 0
 clang_version_minor = 0
 
 if clang_path:
-  clang_version_out = subprocess.Popen([clang_path, "--version"],
-    stdout=subprocess.PIPE).communicate()[0]
-  clang_version_match = re.search(CLANG_VERSION_REGEX, clang_version_out)
-  clang_version_str = clang_version_match.group(1)
-  clang_version_array = clang_version_str.split('.')
-  clang_version_major = int(clang_version_array[0])
-  clang_version_minor = int(clang_version_array[1])
+    clang_version_out = subprocess.Popen([clang_path, "--version"], stdout=subprocess.PIPE).communicate()[0]
+    clang_version_match = re.search(CLANG_VERSION_REGEX, clang_version_out)
+    clang_version_str = clang_version_match.group(1)
+    clang_version_array = clang_version_str.split('.')
+    clang_version_major = int(clang_version_array[0])
+    clang_version_minor = int(clang_version_array[1])
 
 if clang_version_major >= 3 and clang_version_minor >= 5:
-  print ""
+    print ""
 else:
-  # Loop in support clang version only
-  clang_version_major = 3
-  clang_version_minor = 9
-  while clang_version_major >= 3 and clang_version_minor >= 5:
-    clang_version_str = "%d.%d" % (clang_version_major, clang_version_minor)
-    clang_path = which("clang++-" + clang_version_str)
-    if clang_path:
-      print clang_version_str
-      sys.exit(0)
-    clang_version_minor -= 1
-    if clang_version_minor < 0:
-      clang_version_minor = 9
-      clang_version_major -= 1
-  print "None"
+    # Loop in support clang version only
+    clang_version_major = 3
+    clang_version_minor = 9
+    while clang_version_major >= 3 and clang_version_minor >= 5:
+        clang_version_str = "%d.%d" % (clang_version_major, clang_version_minor)
+        clang_path = which("clang++-" + clang_version_str)
+        if clang_path:
+            print clang_version_str
+            sys.exit(0)
+        clang_version_minor -= 1
+        if clang_version_minor < 0:
+            clang_version_minor = 9
+            clang_version_major -= 1
+    print "None"
diff --git a/common/Android.bp b/common/Android.bp
index 587b197..e05528c 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -12,6 +12,7 @@
     srcs: [
         "address_obfuscator.cc",
         "message_loop_thread.cc",
+        "metric_id_allocator.cc",
         "metrics.cc",
         "once_timer.cc",
         "repeating_timer.cc",
@@ -40,8 +41,10 @@
     srcs: [
         "address_obfuscator_unittest.cc",
         "leaky_bonded_queue_unittest.cc",
+        "lru_unittest.cc",
         "message_loop_thread_unittest.cc",
         "metrics_unittest.cc",
+        "metric_id_allocator_unittest.cc",
         "once_timer_unittest.cc",
         "repeating_timer_unittest.cc",
         "state_machine_unittest.cc",
diff --git a/common/lru.h b/common/lru.h
new file mode 100644
index 0000000..36f8707
--- /dev/null
+++ b/common/lru.h
@@ -0,0 +1,188 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <functional>
+#include <iterator>
+#include <list>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <unordered_map>
+
+#include <base/logging.h>
+
+namespace bluetooth {
+
+namespace common {
+
+template <typename K, typename V>
+class LruCache {
+ public:
+  using Node = std::pair<K, V>;
+  /**
+   * Constructor of the cache
+   *
+   * @param capacity maximum size of the cache
+   * @param log_tag, keyword to put at the head of log.
+   */
+  LruCache(const size_t& capacity, const std::string& log_tag)
+      : capacity_(capacity) {
+    if (capacity_ == 0) {
+      // don't allow invalid capacity
+      LOG(FATAL) << log_tag << " unable to have 0 LRU Cache capacity";
+    }
+  }
+
+  // delete copy constructor
+  LruCache(LruCache const&) = delete;
+  LruCache& operator=(LruCache const&) = delete;
+
+  ~LruCache() { Clear(); }
+
+  /**
+   * Clear the cache
+   */
+  void Clear() {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    lru_map_.clear();
+    node_list_.clear();
+  }
+
+  /**
+   * Same as Get, but return an iterator to the accessed element
+   *
+   * Modifying the returned iterator does not warm up the cache
+   *
+   * @param key
+   * @return pointer to the underlying value to allow in-place modification
+   * nullptr when not found, will be invalidated when the key is evicted
+   */
+  V* Find(const K& key) {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    auto map_iterator = lru_map_.find(key);
+    if (map_iterator == lru_map_.end()) {
+      return nullptr;
+    }
+    node_list_.splice(node_list_.begin(), node_list_, map_iterator->second);
+    return &(map_iterator->second->second);
+  }
+
+  /**
+   * Get the value of a key, and move the key to the head of cache, if there is
+   * one
+   *
+   * @param key
+   * @param value, output parameter of value of the key
+   * @return true if the cache has the key
+   */
+  bool Get(const K& key, V* value) {
+    CHECK(value != nullptr);
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    auto value_ptr = Find(key);
+    if (value_ptr == nullptr) {
+      return false;
+    }
+    *value = *value_ptr;
+    return true;
+  }
+
+  /**
+   * Check if the cache has the input key, move the key to the head
+   * if there is one
+   *
+   * @param key
+   * @return true if the cache has the key
+   */
+  bool HasKey(const K& key) {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    return Find(key) != nullptr;
+  }
+
+  /**
+   * Put a key-value pair to the head of cache
+   *
+   * @param key
+   * @param value
+   * @return evicted node if tail value is popped, std::nullopt if no value
+   * is popped. std::optional can be treated as a boolean as well
+   */
+  std::optional<Node> Put(const K& key, V value) {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    auto value_ptr = Find(key);
+    if (value_ptr != nullptr) {
+      // hasKey() calls get(), therefore already move the node to the head
+      *value_ptr = std::move(value);
+      return std::nullopt;
+    }
+
+    // remove tail
+    std::optional<Node> ret = std::nullopt;
+    if (lru_map_.size() == capacity_) {
+      lru_map_.erase(node_list_.back().first);
+      ret = std::move(node_list_.back());
+      node_list_.pop_back();
+    }
+    // insert to dummy next;
+    node_list_.emplace_front(key, std::move(value));
+    lru_map_.emplace(key, node_list_.begin());
+    return ret;
+  }
+
+  /**
+   * Delete a key from cache
+   *
+   * @param key
+   * @return true if deleted successfully
+   */
+  bool Remove(const K& key) {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    auto map_iterator = lru_map_.find(key);
+    if (map_iterator == lru_map_.end()) {
+      return false;
+    }
+
+    // remove from the list
+    node_list_.erase(map_iterator->second);
+
+    // delete key from map
+    lru_map_.erase(map_iterator);
+
+    return true;
+  }
+
+  /**
+   * Return size of the cache
+   *
+   * @return size of the cache
+   */
+  int Size() const {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    return lru_map_.size();
+  }
+
+ private:
+  std::list<Node> node_list_;
+  size_t capacity_;
+  std::unordered_map<K, typename std::list<Node>::iterator> lru_map_;
+  mutable std::recursive_mutex lru_mutex_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/common/lru_unittest.cc b/common/lru_unittest.cc
new file mode 100644
index 0000000..6e1d5e4
--- /dev/null
+++ b/common/lru_unittest.cc
@@ -0,0 +1,260 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <chrono>
+#include <limits>
+
+#include "common/lru.h"
+
+namespace testing {
+
+using bluetooth::common::LruCache;
+
+TEST(BluetoothLruCacheTest, LruCacheMainTest1) {
+  int* value = new int(0);
+  LruCache<int, int> cache(3, "testing");  // capacity = 3;
+  cache.Put(1, 10);
+  EXPECT_EQ(cache.Size(), 1);
+  EXPECT_FALSE(cache.Put(2, 20));
+  EXPECT_FALSE(cache.Put(3, 30));
+  EXPECT_EQ(cache.Size(), 3);
+
+  // 1, 2, 3 should be in cache
+  EXPECT_TRUE(cache.Get(1, value));
+  EXPECT_EQ(*value, 10);
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 20);
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+  EXPECT_EQ(cache.Size(), 3);
+
+  EXPECT_THAT(cache.Put(4, 40), Optional(Pair(1, 10)));
+  // 2, 3, 4 should be in cache, 1 is evicted
+  EXPECT_FALSE(cache.Get(1, value));
+  EXPECT_TRUE(cache.Get(4, value));
+  EXPECT_EQ(*value, 40);
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 20);
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+
+  EXPECT_THAT(cache.Put(5, 50), Optional(Pair(4, 40)));
+  EXPECT_EQ(cache.Size(), 3);
+  // 2, 3, 5 should be in cache, 4 is evicted
+
+  EXPECT_TRUE(cache.Remove(3));
+  EXPECT_FALSE(cache.Put(6, 60));
+  // 2, 5, 6 should be in cache
+
+  EXPECT_FALSE(cache.Get(3, value));
+  EXPECT_FALSE(cache.Get(4, value));
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 20);
+  EXPECT_TRUE(cache.Get(5, value));
+  EXPECT_EQ(*value, 50);
+  EXPECT_TRUE(cache.Get(6, value));
+  EXPECT_EQ(*value, 60);
+}
+
+TEST(BluetoothLruCacheTest, LruCacheMainTest2) {
+  int* value = new int(0);
+  LruCache<int, int> cache(2, "testing");  // size = 2;
+  EXPECT_FALSE(cache.Put(1, 10));
+  EXPECT_FALSE(cache.Put(2, 20));
+  EXPECT_THAT(cache.Put(3, 30), Optional(Pair(1, 10)));
+  EXPECT_FALSE(cache.Put(2, 200));
+  EXPECT_EQ(cache.Size(), 2);
+  // 3, 2 should be in cache
+
+  EXPECT_FALSE(cache.HasKey(1));
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 200);
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+
+  EXPECT_THAT(cache.Put(4, 40), Optional(Pair(2, 200)));
+  // 3, 4 should be in cache
+
+  EXPECT_FALSE(cache.HasKey(2));
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+  EXPECT_TRUE(cache.Get(4, value));
+  EXPECT_EQ(*value, 40);
+
+  EXPECT_TRUE(cache.Remove(4));
+  EXPECT_EQ(cache.Size(), 1);
+  cache.Put(2, 2000);
+  // 3, 2 should be in cache
+
+  EXPECT_FALSE(cache.HasKey(4));
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 2000);
+
+  EXPECT_TRUE(cache.Remove(2));
+  EXPECT_TRUE(cache.Remove(3));
+  cache.Put(5, 50);
+  cache.Put(1, 100);
+  cache.Put(1, 1000);
+  EXPECT_EQ(cache.Size(), 2);
+  // 1, 5 should be in cache
+
+  EXPECT_FALSE(cache.HasKey(2));
+  EXPECT_FALSE(cache.HasKey(3));
+  EXPECT_TRUE(cache.Get(1, value));
+  EXPECT_EQ(*value, 1000);
+  EXPECT_TRUE(cache.Get(5, value));
+  EXPECT_EQ(*value, 50);
+}
+
+TEST(BluetoothLruCacheTest, LruCacheFindTest) {
+  LruCache<int, int> cache(10, "testing");
+  cache.Put(1, 10);
+  cache.Put(2, 20);
+  int value = 0;
+  EXPECT_TRUE(cache.Get(1, &value));
+  EXPECT_EQ(value, 10);
+  auto value_ptr = cache.Find(1);
+  EXPECT_NE(value_ptr, nullptr);
+  *value_ptr = 20;
+  EXPECT_TRUE(cache.Get(1, &value));
+  EXPECT_EQ(value, 20);
+  cache.Put(1, 40);
+  EXPECT_EQ(*value_ptr, 40);
+  EXPECT_EQ(cache.Find(10), nullptr);
+}
+
+TEST(BluetoothLruCacheTest, LruCacheGetTest) {
+  LruCache<int, int> cache(10, "testing");
+  cache.Put(1, 10);
+  cache.Put(2, 20);
+  int value = 0;
+  EXPECT_TRUE(cache.Get(1, &value));
+  EXPECT_EQ(value, 10);
+  EXPECT_TRUE(cache.HasKey(1));
+  EXPECT_TRUE(cache.HasKey(2));
+  EXPECT_FALSE(cache.HasKey(3));
+  EXPECT_FALSE(cache.Get(3, &value));
+  EXPECT_EQ(value, 10);
+}
+
+TEST(BluetoothLruCacheTest, LruCacheRemoveTest) {
+  LruCache<int, int> cache(10, "testing");
+  for (int key = 0; key <= 30; key++) {
+    cache.Put(key, key * 100);
+  }
+  for (int key = 0; key <= 20; key++) {
+    EXPECT_FALSE(cache.HasKey(key));
+  }
+  for (int key = 21; key <= 30; key++) {
+    EXPECT_TRUE(cache.HasKey(key));
+  }
+  for (int key = 21; key <= 30; key++) {
+    EXPECT_TRUE(cache.Remove(key));
+  }
+  for (int key = 21; key <= 30; key++) {
+    EXPECT_FALSE(cache.HasKey(key));
+  }
+}
+
+TEST(BluetoothLruCacheTest, LruCacheClearTest) {
+  LruCache<int, int> cache(10, "testing");
+  for (int key = 0; key < 10; key++) {
+    cache.Put(key, key * 100);
+  }
+  for (int key = 0; key < 10; key++) {
+    EXPECT_TRUE(cache.HasKey(key));
+  }
+  cache.Clear();
+  for (int key = 0; key < 10; key++) {
+    EXPECT_FALSE(cache.HasKey(key));
+  }
+
+  for (int key = 0; key < 10; key++) {
+    cache.Put(key, key * 1000);
+  }
+  for (int key = 0; key < 10; key++) {
+    EXPECT_TRUE(cache.HasKey(key));
+  }
+}
+
+TEST(BluetoothLruCacheTest, LruCachePressureTest) {
+  auto started = std::chrono::high_resolution_clock::now();
+  int max_size = 0xFFFFF;  // 2^20 = 1M
+  LruCache<int, int> cache(static_cast<size_t>(max_size), "testing");
+
+  // fill the cache
+  for (int key = 0; key < max_size; key++) {
+    cache.Put(key, key);
+  }
+
+  // make sure the cache is full
+  for (int key = 0; key < max_size; key++) {
+    EXPECT_TRUE(cache.HasKey(key));
+  }
+
+  // refresh the entire cache
+  for (int key = 0; key < max_size; key++) {
+    int new_key = key + max_size;
+    cache.Put(new_key, new_key);
+    EXPECT_FALSE(cache.HasKey(key));
+    EXPECT_TRUE(cache.HasKey(new_key));
+  }
+
+  // clear the entire cache
+  int* value = new int(0);
+  for (int key = max_size; key < 2 * max_size; key++) {
+    EXPECT_TRUE(cache.Get(key, value));
+    EXPECT_EQ(*value, key);
+    EXPECT_TRUE(cache.Remove(key));
+  }
+  EXPECT_EQ(cache.Size(), 0);
+
+  // test execution time
+  auto done = std::chrono::high_resolution_clock::now();
+  int execution_time =
+      std::chrono::duration_cast<std::chrono::milliseconds>(done - started)
+          .count();
+  // always around 750ms on flame. 1400 ms on crosshatch, 6800 ms on presubmit
+  // Shouldn't be more than 10000ms
+  EXPECT_LT(execution_time, 10000);
+}
+
+TEST(BluetoothLruCacheTest, BluetoothLruMultiThreadPressureTest) {
+  LruCache<int, int> cache(100, "testing");
+  auto pointer = &cache;
+  // make sure no deadlock
+  std::vector<std::thread> workers;
+  for (int key = 0; key < 100; key++) {
+    workers.push_back(std::thread([key, pointer]() {
+      pointer->Put(key, key);
+      EXPECT_TRUE(pointer->HasKey(key));
+      EXPECT_TRUE(pointer->Remove(key));
+    }));
+  }
+  for (auto& worker : workers) {
+    worker.join();
+  }
+  EXPECT_EQ(cache.Size(), 0);
+}
+
+}  // namespace testing
diff --git a/common/metric_id_allocator.cc b/common/metric_id_allocator.cc
new file mode 100644
index 0000000..49b11cc
--- /dev/null
+++ b/common/metric_id_allocator.cc
@@ -0,0 +1,205 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <base/logging.h>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include "metric_id_allocator.h"
+
+namespace bluetooth {
+
+namespace common {
+
+const std::string MetricIdAllocator::LOGGING_TAG = "BluetoothMetricIdAllocator";
+const size_t MetricIdAllocator::kMaxNumUnpairedDevicesInMemory = 200;
+const size_t MetricIdAllocator::kMaxNumPairedDevicesInMemory = 65000;
+const int MetricIdAllocator::kMinId = 1;
+const int MetricIdAllocator::kMaxId = 65534;  // 2^16 - 2
+
+// id space should always be larger than kMaxNumPairedDevicesInMemory +
+// kMaxNumUnpairedDevicesInMemory
+static_assert((MetricIdAllocator::kMaxNumUnpairedDevicesInMemory +
+               MetricIdAllocator::kMaxNumPairedDevicesInMemory) <
+                  (MetricIdAllocator::kMaxId - MetricIdAllocator::kMinId),
+              "id space should always be larger than "
+              "kMaxNumPairedDevicesInMemory + MaxNumUnpairedDevicesInMemory");
+
+MetricIdAllocator::MetricIdAllocator()
+    : paired_device_cache_(kMaxNumPairedDevicesInMemory, LOGGING_TAG),
+      temporary_device_cache_(kMaxNumUnpairedDevicesInMemory, LOGGING_TAG) {}
+
+bool MetricIdAllocator::Init(
+    const std::unordered_map<RawAddress, int>& paired_device_map,
+    Callback save_id_callback, Callback forget_device_callback) {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  if (initialized_) {
+    return false;
+  }
+
+  // init paired_devices_map
+  if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) {
+    LOG(FATAL)
+        << LOGGING_TAG
+        << "Paired device map is bigger than kMaxNumPairedDevicesInMemory";
+    // fail loudly to let caller know
+    return false;
+  }
+
+  next_id_ = kMinId;
+  for (const auto& p : paired_device_map) {
+    if (p.second < kMinId || p.second > kMaxId) {
+      LOG(FATAL) << LOGGING_TAG << "Invalid Bluetooth Metric Id in config";
+    }
+    auto evicted = paired_device_cache_.Put(p.first, p.second);
+    if (evicted) {
+      ForgetDevicePostprocess(evicted->first, evicted->second);
+    }
+    id_set_.insert(p.second);
+    next_id_ = std::max(next_id_, p.second + 1);
+  }
+  if (next_id_ > kMaxId) {
+    next_id_ = kMinId;
+  }
+
+  // init callbacks
+  save_id_callback_ = save_id_callback;
+  forget_device_callback_ = forget_device_callback;
+
+  return initialized_ = true;
+}
+
+MetricIdAllocator::~MetricIdAllocator() { Close(); }
+
+bool MetricIdAllocator::Close() {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  if (!initialized_) {
+    return false;
+  }
+  paired_device_cache_.Clear();
+  temporary_device_cache_.Clear();
+  id_set_.clear();
+  initialized_ = false;
+  return true;
+}
+
+MetricIdAllocator& MetricIdAllocator::GetInstance() {
+  static MetricIdAllocator metric_id_allocator;
+  return metric_id_allocator;
+}
+
+bool MetricIdAllocator::IsEmpty() const {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  return paired_device_cache_.Size() == 0 &&
+         temporary_device_cache_.Size() == 0;
+}
+
+// call this function when a new device is scanned
+int MetricIdAllocator::AllocateId(const RawAddress& mac_address) {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  int id = 0;
+  // if already have an id, return it
+  if (paired_device_cache_.Get(mac_address, &id)) {
+    return id;
+  }
+  if (temporary_device_cache_.Get(mac_address, &id)) {
+    return id;
+  }
+
+  // find next available id
+  while (id_set_.count(next_id_) > 0) {
+    next_id_++;
+    if (next_id_ > kMaxId) {
+      next_id_ = kMinId;
+      LOG(WARNING) << LOGGING_TAG << "Bluetooth metric id overflow.";
+    }
+  }
+  id = next_id_++;
+  id_set_.insert(id);
+  auto evicted = temporary_device_cache_.Put(mac_address, id);
+  if (evicted) {
+    this->id_set_.erase(evicted->second);
+  }
+
+  if (next_id_ > kMaxId) {
+    next_id_ = kMinId;
+  }
+  return id;
+}
+
+// call this function when a device is paired
+bool MetricIdAllocator::SaveDevice(const RawAddress& mac_address) {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  int id = 0;
+  if (paired_device_cache_.Get(mac_address, &id)) {
+    return true;
+  }
+  if (!temporary_device_cache_.Get(mac_address, &id)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Failed to save device because device is not in "
+               << "temporary_device_cache_";
+    return false;
+  }
+  if (!temporary_device_cache_.Remove(mac_address)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Failed to remove device from temporary_device_cache_";
+    return false;
+  }
+  auto evicted = paired_device_cache_.Put(mac_address, id);
+  if (evicted) {
+    ForgetDevicePostprocess(evicted->first, evicted->second);
+  }
+  if (!save_id_callback_(mac_address, id)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Callback returned false after saving the device";
+    return false;
+  }
+  return true;
+}
+
+// call this function when a device is forgotten
+void MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  int id = 0;
+  if (!paired_device_cache_.Get(mac_address, &id)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Failed to forget device because device is not in "
+               << "paired_device_cache_";
+    return;
+  }
+  if (!paired_device_cache_.Remove(mac_address)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Failed to remove device from paired_device_cache_";
+    return;
+  }
+  ForgetDevicePostprocess(mac_address, id);
+}
+
+bool MetricIdAllocator::IsValidId(const int id) {
+  return id >= kMinId && id <= kMaxId;
+}
+
+void MetricIdAllocator::ForgetDevicePostprocess(const RawAddress& mac_address,
+                                                const int id) {
+  id_set_.erase(id);
+  forget_device_callback_(mac_address, id);
+}
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/common/metric_id_allocator.h b/common/metric_id_allocator.h
new file mode 100644
index 0000000..63236e4
--- /dev/null
+++ b/common/metric_id_allocator.h
@@ -0,0 +1,139 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_set>
+#include "raw_address.h"
+
+#include "lru.h"
+
+namespace bluetooth {
+
+namespace common {
+
+class MetricIdAllocator {
+ public:
+  using Callback = std::function<bool(const RawAddress& address, const int id)>;
+
+  static const size_t kMaxNumUnpairedDevicesInMemory;
+  static const size_t kMaxNumPairedDevicesInMemory;
+
+  static const int kMinId;
+  static const int kMaxId;
+
+  ~MetricIdAllocator();
+
+  /**
+   * Get the instance of singleton
+   *
+   * @return MetricIdAllocator&
+   */
+  static MetricIdAllocator& GetInstance();
+
+  /**
+   * Initialize the allocator
+   *
+   * @param paired_device_map map from mac_address to id already saved
+   * in the disk before init
+   * @param save_id_callback a callback that will be called after successfully
+   * saving id for a paired device
+   * @param forget_device_callback a callback that will be called after
+   * successful id deletion for forgotten device,
+   * @return true if successfully initialized
+   */
+  bool Init(const std::unordered_map<RawAddress, int>& paired_device_map,
+            Callback save_id_callback, Callback forget_device_callback);
+
+  /**
+   * Close the allocator. should be called when Bluetooth process is killed
+   *
+   * @return true if successfully close
+   */
+  bool Close();
+
+  /**
+   * Check if no id saved in memory
+   *
+   * @return true if no id is saved
+   */
+  bool IsEmpty() const;
+
+  /**
+   * Allocate an id for a scanned device, or return the id if there is already
+   * one
+   *
+   * @param mac_address mac address of Bluetooth device
+   * @return the id of device
+   */
+  int AllocateId(const RawAddress& mac_address);
+
+  /**
+   * Save the id for a paired device
+   *
+   * @param mac_address mac address of Bluetooth device
+   * @return true if save successfully
+   */
+  bool SaveDevice(const RawAddress& mac_address);
+
+  /**
+   * Delete the id for a device to be forgotten
+   *
+   * @param mac_address mac address of Bluetooth device
+   */
+  void ForgetDevice(const RawAddress& mac_address);
+
+  /**
+   * Check if an id is valid.
+   * The id should be less than or equal to kMaxId and bigger than or equal to
+   * kMinId
+   *
+   * @param mac_address mac address of Bluetooth device
+   * @return true if delete successfully
+   */
+  static bool IsValidId(const int id);
+
+ protected:
+  // Singleton
+  MetricIdAllocator();
+
+ private:
+  static const std::string LOGGING_TAG;
+  mutable std::mutex id_allocator_mutex_;
+
+  LruCache<RawAddress, int> paired_device_cache_;
+  LruCache<RawAddress, int> temporary_device_cache_;
+  std::unordered_set<int> id_set_;
+
+  int next_id_{kMinId};
+  bool initialized_{false};
+  Callback save_id_callback_;
+  Callback forget_device_callback_;
+
+  void ForgetDevicePostprocess(const RawAddress& mac_address, const int id);
+
+  // delete copy constructor for singleton
+  MetricIdAllocator(MetricIdAllocator const&) = delete;
+  MetricIdAllocator& operator=(MetricIdAllocator const&) = delete;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/common/metric_id_allocator_unittest.cc b/common/metric_id_allocator_unittest.cc
new file mode 100644
index 0000000..ccc1576
--- /dev/null
+++ b/common/metric_id_allocator_unittest.cc
@@ -0,0 +1,473 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+#include "common/metric_id_allocator.h"
+
+namespace testing {
+
+using bluetooth::common::MetricIdAllocator;
+
+RawAddress kthAddress(uint32_t k) {
+  uint8_t array[6] = {0, 0, 0, 0, 0, 0};
+  for (int i = 5; i >= 2; i--) {
+    array[i] = k % 256;
+    k = k / 256;
+  }
+  RawAddress addr(array);
+  return addr;
+}
+
+std::unordered_map<RawAddress, int> generateAddresses(const uint32_t num) {
+  // generate first num of mac address -> id pairs
+  // input may is always valid 256^6 = 2^48 > 2^32
+  std::unordered_map<RawAddress, int> device_map;
+  for (size_t key = 0; key < num; key++) {
+    device_map[kthAddress(key)] = key + MetricIdAllocator::kMinId;
+  }
+  return device_map;
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorInitCloseTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+  EXPECT_FALSE(allocator.Init(paired_device_map, callback, callback));
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorNotCloseTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+
+  // should fail because it isn't closed
+  EXPECT_FALSE(allocator.Init(paired_device_map, callback, callback));
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorScanDeviceFromEmptyTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  // test empty map, next id should be kMinId
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+  EXPECT_EQ(allocator.AllocateId(kthAddress(0)), MetricIdAllocator::kMinId);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(1)), MetricIdAllocator::kMinId + 1);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(0)), MetricIdAllocator::kMinId);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(2)), MetricIdAllocator::kMinId + 2);
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest,
+     MetricIdAllocatorScanDeviceFromFilledTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  int id = static_cast<int>(MetricIdAllocator::kMaxNumPairedDevicesInMemory) +
+           MetricIdAllocator::kMinId;
+  // next id should be MetricIdAllocator::kMaxNumPairedDevicesInMemory
+  paired_device_map =
+      generateAddresses(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+  // try new values not in the map, should get new id.
+  EXPECT_EQ(allocator.AllocateId(kthAddress(INT_MAX)), id);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(INT_MAX - 1)), id + 1);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(INT_MAX)), id);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(INT_MAX - 2)), id + 2);
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorAllocateExistingTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map =
+      generateAddresses(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
+
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  int id = MetricIdAllocator::kMinId;
+  // next id should be MetricIdAllocator::kMaxNumPairedDevicesInMemory
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+
+  // try values already in the map, should get new id.
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 0})), id);
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 1})), id + 1);
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 0})), id);
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 2})), id + 2);
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorMainTest1) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  int dummy = 22;
+  int* pointer = &dummy;
+  MetricIdAllocator::Callback save_callback = [pointer](const RawAddress&,
+                                                        const int) {
+    *pointer = *pointer * 2;
+    return true;
+  };
+  MetricIdAllocator::Callback forget_callback = [pointer](const RawAddress&,
+                                                          const int) {
+    *pointer = *pointer / 2;
+    return true;
+  };
+
+  EXPECT_TRUE(
+      allocator.Init(paired_device_map, save_callback, forget_callback));
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 0})),
+            MetricIdAllocator::kMinId);
+  // save it and make sure the callback is called
+  EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 0})));
+  EXPECT_EQ(dummy, 44);
+
+  // should fail, since id of device is not allocated
+  EXPECT_FALSE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 1})));
+  EXPECT_EQ(dummy, 44);
+
+  // save it and make sure the callback is called
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 2})),
+            MetricIdAllocator::kMinId + 1);
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 3})),
+            MetricIdAllocator::kMinId + 2);
+  EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 2})));
+  EXPECT_EQ(dummy, 88);
+  EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 3})));
+  EXPECT_EQ(dummy, 176);
+
+  // should be true but callback won't be called, since id had been saved
+  EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 0})));
+  EXPECT_EQ(dummy, 176);
+
+  // forget
+  allocator.ForgetDevice(RawAddress({0, 0, 0, 0, 0, 1}));
+  EXPECT_EQ(dummy, 176);
+  allocator.ForgetDevice(RawAddress({0, 0, 0, 0, 0, 2}));
+  EXPECT_EQ(dummy, 88);
+
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorFullPairedMap) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  // preset a full map
+  std::unordered_map<RawAddress, int> paired_device_map =
+      generateAddresses(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
+  int dummy = 243;
+  int* pointer = &dummy;
+  MetricIdAllocator::Callback save_callback = [pointer](const RawAddress&,
+                                                        const int) {
+    *pointer = *pointer * 2;
+    return true;
+  };
+  MetricIdAllocator::Callback forget_callback = [pointer](const RawAddress&,
+                                                          const int) {
+    *pointer = *pointer / 3;
+    return true;
+  };
+
+  EXPECT_TRUE(
+      allocator.Init(paired_device_map, save_callback, forget_callback));
+
+  // check if all preset ids are there.
+  // comments based on kMaxNumPairedDevicesInMemory = 200. It can change.
+  int key = 0;
+  for (key = 0;
+       key < static_cast<int>(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
+       key++) {
+    EXPECT_EQ(allocator.AllocateId(kthAddress(key)),
+              key + MetricIdAllocator::kMinId);
+  }
+  // paired: 0, 1, 2 ... 199,
+  // scanned:
+
+  int id = static_cast<int>(MetricIdAllocator::kMaxNumPairedDevicesInMemory +
+                            MetricIdAllocator::kMinId);
+  // next id should be MetricIdAllocator::kMaxNumPairedDevicesInMemory +
+  // MetricIdAllocator::kMinId
+
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key)), id++);
+  // paired: 0, 1, 2 ... 199,
+  // scanned: 200
+
+  // save it and make sure the callback is called
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key)));
+  EXPECT_EQ(dummy, 162);  // one key is evicted, another key is saved so *2/3
+
+  // paired: 1, 2 ... 199, 200,
+  // scanned:
+
+  EXPECT_EQ(allocator.AllocateId(kthAddress(0)), id++);
+  // paired: 1, 2 ... 199, 200
+  // scanned: 0
+
+  // key == 200
+  // should fail, since id of device is not allocated
+  EXPECT_FALSE(allocator.SaveDevice(kthAddress(key + 1)));
+  EXPECT_EQ(dummy, 162);
+  // paired: 1, 2 ... 199, 200,
+  // scanned: 0
+
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 1)), id++);
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 1)));
+  EXPECT_EQ(dummy, 108);  // one key is evicted, another key is saved so *2/3,
+  // paired: 2 ... 199, 200, 201
+  // scanned: 0
+
+  EXPECT_EQ(allocator.AllocateId(kthAddress(1)), id++);
+  // paired: 2 ... 199, 200, 201,
+  // scanned: 0, 1
+
+  // save it and make sure the callback is called
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 2)), id++);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 3)), id++);
+  // paired: 2 ... 199, 200, 201,
+  // scanned: 0, 1, 202, 203
+
+  dummy = 9;
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 2)));
+  EXPECT_EQ(dummy, 6);  // one key is evicted, another key is saved so *2/3,
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 3)));
+  EXPECT_EQ(dummy, 4);  // one key is evicted, another key is saved so *2/3,
+  // paired: 4 ... 199, 200, 201, 202, 203
+  // scanned: 0, 1
+
+  // should be true but callback won't be called, since id had been saved
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 2)));
+  EXPECT_EQ(dummy, 4);
+
+  dummy = 27;
+  // forget
+  allocator.ForgetDevice(kthAddress(key + 200));
+  EXPECT_EQ(dummy, 27);  // should fail, no such a key
+  allocator.ForgetDevice(kthAddress(key + 2));
+  EXPECT_EQ(dummy, 9);
+  // paired: 4 ... 199, 200, 201, 203
+  // scanned: 0, 1
+
+  // save it and make sure the callback is called
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 2)), id++);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 4)), id++);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 5)), id++);
+  // paired: 4 ... 199, 200, 201, 203
+  // scanned: 0, 1, 202, 204, 205
+
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 2)));
+  EXPECT_EQ(dummy, 18);  // no key is evicted, a key is saved so *2,
+
+  // should be true but callback won't be called, since id had been saved
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 3)));
+  EXPECT_EQ(dummy, 18);  // no such a key in scanned
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 4)));
+  EXPECT_EQ(dummy, 12);  // one key is evicted, another key is saved so *2/3,
+  // paired: 5 6 ... 199, 200, 201, 203, 202, 204
+  // scanned: 0, 1, 205
+
+  // verify paired:
+  for (key = 5; key <= 199; key++) {
+    dummy = 3;
+    allocator.ForgetDevice(kthAddress(key));
+    EXPECT_EQ(dummy, 1);
+  }
+  for (size_t k = MetricIdAllocator::kMaxNumPairedDevicesInMemory;
+       k <= MetricIdAllocator::kMaxNumPairedDevicesInMemory + 4; k++) {
+    dummy = 3;
+    allocator.ForgetDevice(kthAddress(k));
+    EXPECT_EQ(dummy, 1);
+  }
+
+  // verify scanned
+  dummy = 4;
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(0)));
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(1)));
+  EXPECT_TRUE(allocator.SaveDevice(
+      kthAddress(MetricIdAllocator::kMaxNumPairedDevicesInMemory + 5)));
+  EXPECT_EQ(dummy, 32);
+
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorFullScannedMap) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  int dummy = 22;
+  int* pointer = &dummy;
+  MetricIdAllocator::Callback save_callback = [pointer](const RawAddress&,
+                                                        const int) {
+    *pointer = *pointer * 2;
+    return true;
+  };
+  MetricIdAllocator::Callback forget_callback = [pointer](const RawAddress&,
+                                                          const int) {
+    *pointer = *pointer / 2;
+    return true;
+  };
+
+  EXPECT_TRUE(
+      allocator.Init(paired_device_map, save_callback, forget_callback));
+
+  // allocate kMaxNumUnpairedDevicesInMemory ids
+  // comments based on kMaxNumUnpairedDevicesInMemory = 200
+  for (int key = 0;
+       key <
+       static_cast<int>(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory);
+       key++) {
+    EXPECT_EQ(allocator.AllocateId(kthAddress(key)),
+              key + MetricIdAllocator::kMinId);
+  }
+  // scanned: 0, 1, 2 ... 199,
+  // paired:
+
+  int id = MetricIdAllocator::kMaxNumUnpairedDevicesInMemory +
+           MetricIdAllocator::kMinId;
+  RawAddress addr =
+      kthAddress(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory);
+  EXPECT_EQ(allocator.AllocateId(addr), id);
+  // scanned: 1, 2 ... 199, 200
+
+  // save it and make sure the callback is called
+  EXPECT_TRUE(allocator.SaveDevice(addr));
+  EXPECT_EQ(allocator.AllocateId(addr), id);
+  EXPECT_EQ(dummy, 44);
+  // paired: 200,
+  // scanned: 1, 2 ... 199,
+  id++;
+
+  addr = kthAddress(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory + 1);
+  EXPECT_EQ(allocator.AllocateId(addr), id++);
+  // paired: 200,
+  // scanned: 1, 2 ... 199, 201
+
+  // try to allocate for device 0, 1, 2, 3, 4....199
+  // we should have a new id every time,
+  // since the scanned map is full at this point
+  for (int key = 0;
+       key <
+       static_cast<int>(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory);
+       key++) {
+    EXPECT_EQ(allocator.AllocateId(kthAddress(key)), id++);
+  }
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorMultiThreadPressureTest) {
+  std::unordered_map<RawAddress, int> paired_device_map;
+  auto& allocator = MetricIdAllocator::GetInstance();
+  int dummy = 22;
+  int* pointer = &dummy;
+  MetricIdAllocator::Callback save_callback = [pointer](const RawAddress&,
+                                                        const int) {
+    *pointer = *pointer + 1;
+    return true;
+  };
+  MetricIdAllocator::Callback forget_callback = [pointer](const RawAddress&,
+                                                          const int) {
+    *pointer = *pointer - 1;
+    return true;
+  };
+  EXPECT_TRUE(
+      allocator.Init(paired_device_map, save_callback, forget_callback));
+
+  // make sure no deadlock
+  std::vector<std::thread> workers;
+  for (int key = 0;
+       key <
+       static_cast<int>(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory);
+       key++) {
+    workers.push_back(std::thread([key]() {
+      auto& allocator = MetricIdAllocator::GetInstance();
+      RawAddress fake_mac_address = kthAddress(key);
+      allocator.AllocateId(fake_mac_address);
+      EXPECT_TRUE(allocator.SaveDevice(fake_mac_address));
+      allocator.ForgetDevice(fake_mac_address);
+    }));
+  }
+  for (auto& worker : workers) {
+    worker.join();
+  }
+  EXPECT_TRUE(allocator.IsEmpty());
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorWrapAroundTest1) {
+  std::unordered_map<RawAddress, int> paired_device_map;
+  auto& allocator = MetricIdAllocator::GetInstance();
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+
+  // make a sparse paired_device_map
+  int min_id = MetricIdAllocator::kMinId;
+  paired_device_map[kthAddress(min_id)] = min_id;
+  paired_device_map[kthAddress(min_id + 1)] = min_id + 1;
+  paired_device_map[kthAddress(min_id + 3)] = min_id + 3;
+  paired_device_map[kthAddress(min_id + 4)] = min_id + 4;
+
+  int max_id = MetricIdAllocator::kMaxId;
+  paired_device_map[kthAddress(max_id - 3)] = max_id - 3;
+  paired_device_map[kthAddress(max_id - 4)] = max_id - 4;
+
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+
+  // next id should be max_id - 2, max_id - 1, max_id, min_id + 2, min_id + 5
+  EXPECT_EQ(allocator.AllocateId(kthAddress(max_id - 2)), max_id - 2);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(max_id - 1)), max_id - 1);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(max_id)), max_id);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(min_id + 2)), min_id + 2);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(min_id + 5)), min_id + 5);
+
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorWrapAroundTest2) {
+  std::unordered_map<RawAddress, int> paired_device_map;
+  auto& allocator = MetricIdAllocator::GetInstance();
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+
+  // make a sparse paired_device_map
+  int min_id = MetricIdAllocator::kMinId;
+  int max_id = MetricIdAllocator::kMaxId;
+  paired_device_map[kthAddress(max_id)] = max_id;
+
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+
+  // next id should be min_id, min_id + 1
+  EXPECT_EQ(allocator.AllocateId(kthAddress(min_id)), min_id);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(min_id + 1)), min_id + 1);
+
+  EXPECT_TRUE(allocator.Close());
+}
+
+}  // namespace testing
diff --git a/common/metrics.cc b/common/metrics.cc
index 4550eeb..5d2365f 100644
--- a/common/metrics.cc
+++ b/common/metrics.cc
@@ -37,6 +37,7 @@
 
 #include "address_obfuscator.h"
 #include "leaky_bonded_queue.h"
+#include "metric_id_allocator.h"
 #include "metrics.h"
 #include "time_util.h"
 
@@ -576,8 +577,10 @@
                                  uint16_t hci_event, uint16_t hci_ble_event,
                                  uint16_t cmd_status, uint16_t reason_code) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (address != nullptr) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(*address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(*address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -586,7 +589,7 @@
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_LINK_LAYER_CONNECTION_EVENT, bytes_field,
       connection_handle, direction, link_type, hci_cmd, hci_event,
-      hci_ble_event, cmd_status, reason_code);
+      hci_ble_event, cmd_status, reason_code, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed to log status " << loghex(cmd_status)
                  << ", reason " << loghex(reason_code) << " from cmd "
@@ -624,8 +627,10 @@
                                uint64_t encoding_interval_millis,
                                int num_missing_pcm_bytes) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -634,7 +639,7 @@
   int64_t encoding_interval_nanos = encoding_interval_millis * 1000000;
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_A2DP_AUDIO_UNDERRUN_REPORTED, bytes_field,
-      encoding_interval_nanos, num_missing_pcm_bytes);
+      encoding_interval_nanos, num_missing_pcm_bytes, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address
                  << ", encoding_interval_nanos " << encoding_interval_nanos
@@ -649,8 +654,10 @@
                               int num_dropped_encoded_frames,
                               int num_dropped_encoded_bytes) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -660,7 +667,7 @@
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_A2DP_AUDIO_OVERRUN_REPORTED, bytes_field,
       encoding_interval_nanos, num_dropped_buffers, num_dropped_encoded_frames,
-      num_dropped_encoded_bytes);
+      num_dropped_encoded_bytes, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed to log for " << address
                  << ", encoding_interval_nanos " << encoding_interval_nanos
@@ -674,16 +681,18 @@
 void LogReadRssiResult(const RawAddress& address, uint16_t handle,
                        uint32_t cmd_status, int8_t rssi) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
       address.IsEmpty() ? nullptr : obfuscated_id.c_str(),
       address.IsEmpty() ? 0 : obfuscated_id.size());
-  int ret =
-      android::util::stats_write(android::util::BLUETOOTH_DEVICE_RSSI_REPORTED,
-                                 bytes_field, handle, cmd_status, rssi);
+  int ret = android::util::stats_write(
+      android::util::BLUETOOTH_DEVICE_RSSI_REPORTED, bytes_field, handle,
+      cmd_status, rssi, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", handle "
                  << handle << ", status " << loghex(cmd_status) << ", rssi "
@@ -695,8 +704,10 @@
                                        uint16_t handle, uint32_t cmd_status,
                                        int32_t failed_contact_counter) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -704,7 +715,7 @@
       address.IsEmpty() ? 0 : obfuscated_id.size());
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_DEVICE_FAILED_CONTACT_COUNTER_REPORTED,
-      bytes_field, handle, cmd_status, failed_contact_counter);
+      bytes_field, handle, cmd_status, failed_contact_counter, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", handle "
                  << handle << ", status " << loghex(cmd_status)
@@ -717,8 +728,10 @@
                                uint32_t cmd_status,
                                int32_t transmit_power_level) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -726,7 +739,7 @@
       address.IsEmpty() ? 0 : obfuscated_id.size());
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_DEVICE_TX_POWER_LEVEL_REPORTED, bytes_field,
-      handle, cmd_status, transmit_power_level);
+      handle, cmd_status, transmit_power_level, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", handle "
                  << handle << ", status " << loghex(cmd_status)
@@ -739,8 +752,10 @@
                         android::bluetooth::DirectionEnum direction,
                         uint8_t smp_fail_reason) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
@@ -748,7 +763,7 @@
       address.IsEmpty() ? 0 : obfuscated_id.size());
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_SMP_PAIRING_EVENT_REPORTED, obfuscated_id_field,
-      smp_cmd, direction, smp_fail_reason);
+      smp_cmd, direction, smp_fail_reason, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", smp_cmd "
                  << loghex(smp_cmd) << ", direction " << direction
@@ -760,15 +775,19 @@
 void LogClassicPairingEvent(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, uint16_t hci_event,
                             uint16_t cmd_status, uint16_t reason_code, int64_t event_value) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
       address.IsEmpty() ? nullptr : obfuscated_id.c_str(),
       address.IsEmpty() ? 0 : obfuscated_id.size());
-  int ret = android::util::stats_write(android::util::BLUETOOTH_CLASSIC_PAIRING_EVENT_REPORTED, obfuscated_id_field,
-                                       handle, hci_cmd, hci_event, cmd_status, reason_code, event_value);
+  int ret = android::util::stats_write(
+      android::util::BLUETOOTH_CLASSIC_PAIRING_EVENT_REPORTED,
+      obfuscated_id_field, handle, hci_cmd, hci_event, cmd_status, reason_code,
+      event_value, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", handle " << handle << ", hci_cmd " << loghex(hci_cmd)
                  << ", hci_event " << loghex(hci_event) << ", cmd_status " << loghex(cmd_status) << ", reason "
@@ -780,8 +799,10 @@
                      uint16_t attribute_id, size_t attribute_size,
                      const char* attribute_value) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
@@ -790,7 +811,7 @@
   android::util::BytesField attribute_field(attribute_value, attribute_size);
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_SDP_ATTRIBUTE_REPORTED, obfuscated_id_field,
-      protocol_uuid, attribute_id, attribute_field);
+      protocol_uuid, attribute_id, attribute_field, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", protocol_uuid "
                  << loghex(protocol_uuid) << ", attribute_id "
@@ -804,8 +825,10 @@
     int64_t tx_bytes, int64_t rx_bytes, int uid, int server_port,
     android::bluetooth::SocketRoleEnum socket_role) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
@@ -814,7 +837,7 @@
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_SOCKET_CONNECTION_STATE_CHANGED,
       obfuscated_id_field, port, type, connection_state, tx_bytes, rx_bytes,
-      uid, server_port, socket_role);
+      uid, server_port, socket_role, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", port " << port
                  << ", type " << type << ", state " << connection_state
@@ -832,8 +855,10 @@
                          const std::string& hardware_version,
                          const std::string& software_version) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
@@ -842,7 +867,7 @@
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_DEVICE_INFO_REPORTED, obfuscated_id_field,
       source_type, source_name.c_str(), manufacturer.c_str(), model.c_str(),
-      hardware_version.c_str(), software_version.c_str());
+      hardware_version.c_str(), software_version.c_str(), metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", source_type "
                  << source_type << ", source_name " << source_name
diff --git a/common/repeating_timer_unittest.cc b/common/repeating_timer_unittest.cc
index 20d8d67..50520cd 100644
--- a/common/repeating_timer_unittest.cc
+++ b/common/repeating_timer_unittest.cc
@@ -27,7 +27,7 @@
 using bluetooth::common::RepeatingTimer;
 
 // Allowed error between the expected and actual delay for DoInThreadDelayed().
-constexpr uint32_t delay_error_ms = 3;
+constexpr uint32_t delay_error_ms = 100;
 
 /**
  * Unit tests to verify Task Scheduler.
diff --git a/device/include/controller.h b/device/include/controller.h
index a15f57b..2127b27 100644
--- a/device/include/controller.h
+++ b/device/include/controller.h
@@ -22,9 +22,9 @@
 #include <stdint.h>
 
 #include "device_features.h"
-#include "hci_layer.h"
-#include "hci_packet_factory.h"
-#include "hci_packet_parser.h"
+#include "hci/include/hci_layer.h"
+#include "hci/include/hci_packet_factory.h"
+#include "hci/include/hci_packet_parser.h"
 
 static const char CONTROLLER_MODULE[] = "controller_module";
 
diff --git a/device/src/controller.cc b/device/src/controller.cc
index 537216b..a260b98 100644
--- a/device/src/controller.cc
+++ b/device/src/controller.cc
@@ -53,7 +53,7 @@
 #define BLE_SUPPORTED_FEATURES_SIZE 8
 #define MAX_LOCAL_SUPPORTED_CODECS_SIZE 8
 
-static const hci_t* hci;
+static const hci_t* local_hci;
 static const hci_packet_factory_t* packet_factory;
 static const hci_packet_parser_t* packet_parser;
 
@@ -90,7 +90,8 @@
 static bool secure_connections_supported;
 
 #define AWAIT_COMMAND(command) \
-  static_cast<BT_HDR*>(future_await(hci->transmit_command_futured(command)))
+  static_cast<BT_HDR*>(        \
+      future_await(local_hci->transmit_command_futured(command)))
 
 // Module lifecycle functions
 
@@ -589,7 +590,7 @@
   if (!loaded) {
     loaded = true;
 
-    hci = hci_layer_get_interface();
+    local_hci = hci_layer_get_interface();
     packet_factory = hci_packet_factory_get_interface();
     packet_parser = hci_packet_parser_get_interface();
   }
@@ -609,7 +610,7 @@
     const hci_t* hci_interface,
     const hci_packet_factory_t* packet_factory_interface,
     const hci_packet_parser_t* packet_parser_interface) {
-  hci = hci_interface;
+  local_hci = hci_interface;
   packet_factory = packet_factory_interface;
   packet_parser = packet_parser_interface;
   return &interface;
diff --git a/device/src/interop.cc b/device/src/interop.cc
index 9a701b1..21744b2 100644
--- a/device/src/interop.cc
+++ b/device/src/interop.cc
@@ -50,9 +50,8 @@
 
   if (interop_match_fixed_(feature, addr) ||
       interop_match_dynamic_(feature, addr)) {
-    LOG_INFO(LOG_TAG, "%s() Device %s is a match for interop workaround %s.",
-             __func__, addr->ToString().c_str(),
-             interop_feature_string_(feature));
+    LOG_INFO("%s() Device %s is a match for interop workaround %s.", __func__,
+             addr->ToString().c_str(), interop_feature_string_(feature));
     return true;
   }
 
@@ -69,8 +68,8 @@
         strlen(name) >= interop_name_database[i].length &&
         strncmp(name, interop_name_database[i].name,
                 interop_name_database[i].length) == 0) {
-      LOG_INFO(LOG_TAG, "%s() Device %s is a match for interop workaround %s.",
-             __func__, name, interop_feature_string_(feature));
+      LOG_INFO("%s() Device %s is a match for interop workaround %s.", __func__,
+               name, interop_feature_string_(feature));
       return true;
     }
   }
diff --git a/embdrv/sbc/encoder/srce/sbc_packing.c b/embdrv/sbc/encoder/srce/sbc_packing.c
index b478a80..205b373 100644
--- a/embdrv/sbc/encoder/srce/sbc_packing.c
+++ b/embdrv/sbc/encoder/srce/sbc_packing.c
@@ -28,26 +28,22 @@
 #if (SBC_ARM_ASM_OPT == TRUE)
 #define Mult32(s32In1, s32In2, s32OutLow)    \
   {                                          \
-    __asm {                                                                                    \
+    __asm {                                  \
         MUL s32OutLow,s32In1,s32In2; } \
   }
 #define Mult64(s32In1, s32In2, s32OutLow, s32OutHi)     \
   {                                                     \
-    __asm {														    						\
+    __asm {                                             \
         SMULL s32OutLow,s32OutHi,s32In1,s32In2 } \
   }
 #else
 #define Mult32(s32In1, s32In2, s32OutLow) \
   s32OutLow = (int32_t)(s32In1) * (int32_t)(s32In2);
-#define Mult64(s32In1, s32In2, s32OutLow, s32OutHi)                   \
-  {                                                                   \
-    (s32OutLow) = ((int32_t)(uint16_t)(s32In1) * (uint16_t)(s32In2)); \
-    s32TempVal2 = (int32_t)(((s32In1) >> 16) * (uint16_t)(s32In2));   \
-    s32Carry = ((((uint32_t)(s32OutLow) >> 16) & 0xFFFF) +            \
-                +(s32TempVal2 & 0xFFFF)) >>                           \
-               16;                                                    \
-    (s32OutLow) += (s32TempVal2 << 16);                               \
-    (s32OutHi) = (s32TempVal2 >> 16) + s32Carry;                      \
+#define Mult64(s32In1, s32In2, s32OutLow, s32OutHi)                \
+  {                                                                \
+    __builtin_mul_overflow(s32In1, (uint16_t)s32In2, &s64OutTemp); \
+    s32OutLow = s64OutTemp & 0xFFFFFFFF;                           \
+    s32OutHi = (s64OutTemp >> 32) & 0xFFFFFFFF;                    \
   }
 #endif
 
@@ -77,7 +73,10 @@
   int32_t s32Temp1;   /*used in 64-bit multiplication*/
   int32_t s32Low;     /*used in 64-bit multiplication*/
 #if (SBC_IS_64_MULT_IN_QUANTIZER == TRUE)
-  int32_t s32Hi1, s32Low1, s32Carry, s32TempVal2, s32Hi, s32Temp2;
+  int32_t s32Hi1, s32Low1, s32Hi, s32Temp2;
+#if (SBC_ARM_ASM_OPT != TRUE)
+  int64_t s64OutTemp;
+#endif
 #endif
 
   pu8PacketPtr = output;           /*Initialize the ptr*/
@@ -141,7 +140,7 @@
         u16Levels = (uint16_t)(((uint32_t)1 << s32LoopCount) - 1);
 
         /* quantizer */
-        s32Temp1 = (*ps32SbPtr >> 2) + (u32SfRaisedToPow2 << 12);
+        s32Temp1 = (*ps32SbPtr >> 2) + (int32_t)(u32SfRaisedToPow2 << 12);
         s32Temp2 = u16Levels;
 
         Mult64(s32Temp1, s32Temp2, s32Low, s32Hi);
diff --git a/gd/.clang-format b/gd/.clang-format
index 1f1f586..edf31fe 100644
--- a/gd/.clang-format
+++ b/gd/.clang-format
@@ -24,5 +24,8 @@
 DerivePointerAlignment: false
 ColumnLimit: 120
 AllowShortFunctionsOnASingleLine: Empty
-ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
 BreakConstructorInitializers: BeforeColon
+AlignAfterOpenBracket: AlwaysBreak
+BinPackArguments: false
+BinPackParameters: false
diff --git a/gd/.gitignore b/gd/.gitignore
index 106fe1e..894b4c5 100644
--- a/gd/.gitignore
+++ b/gd/.gitignore
@@ -1,2 +1,3 @@
 **/default.profraw
 **/__pycache__/
+gd_cert_venv
diff --git a/gd/Android.bp b/gd/Android.bp
index 7020f66..03f4d9a 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -33,6 +33,7 @@
     conlyflags: [
         "-std=c99",
     ],
+    header_libs: ["jni_headers"],
     sanitize: {
         misc_undefined: ["bounds"],
     },
@@ -71,8 +72,37 @@
     },
 }
 
-cc_library {
-    name: "libbluetooth_gd",
+cc_defaults {
+    name: "gd_clang_tidy",
+    tidy: true,
+    tidy_checks: [
+        "cppcoreguidelines-pro-type-member-init",
+        "clang-analyzer-core.CallAndMessage",
+        "clang-analyzer-optin.cplusplus.UninitializedObject",
+        "-google*",
+        "-performance*",
+        "-bugprone*",
+    ],
+    tidy_checks_as_errors: [
+        "cppcoreguidelines-pro-type-member-init",
+        "clang-analyzer-core.CallAndMessage",
+        "clang-analyzer-optin.cplusplus.UninitializedObject",
+    ],
+    tidy_flags: [
+        "--header-filter=^.*system/bt/.*.h$",
+        "--extra-arg-before=-Xclang",
+        "--extra-arg-before=-analyzer-config",
+        "--extra-arg-before=-Xclang",
+        "--extra-arg-before=optin.cplusplus.UninitializedObject:Pedantic=true",
+        "--extra-arg-before=-Xclang",
+        "--extra-arg-before=-analyzer-config",
+        "--extra-arg-before=-Xclang",
+        "--extra-arg-before=optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true",
+    ],
+}
+
+cc_defaults {
+    name: "libbluetooth_gd_defaults",
     defaults: [
         "gd_defaults",
         "gd_clang_file_coverage",
@@ -87,22 +117,26 @@
         host: {
             srcs: [
                 ":BluetoothHalSources_hci_rootcanal",
+                ":BluetoothOsSources_host",
             ],
         },
         android: {
             srcs: [
                 ":BluetoothHalSources_hci_android_hidl",
+                ":BluetoothOsSources_android",
             ],
             shared_libs: [
                 "android.hardware.bluetooth@1.0",
                 "libhidlbase",
                 "libutils",
+                "libcutils",
             ],
         },
     },
     srcs: [
         "stack_manager.cc",
         "module.cc",
+        ":BluetoothAttSources",
         ":BluetoothCommonSources",
         ":BluetoothCryptoToolboxSources",
         ":BluetoothHalSources",
@@ -112,12 +146,41 @@
         ":BluetoothPacketSources",
         ":BluetoothShimSources",
         ":BluetoothSecuritySources",
+        ":BluetoothStorageSources",
     ],
     generated_headers: [
+        "BluetoothGeneratedBundlerSchema_h_bfbs",
+        "BluetoothGeneratedDumpsysDataSchema_h",
+        "BluetoothGeneratedDumpsysModuleSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
     shared_libs: [
         "libchrome",
+        "libcrypto",
+        "libflatbuffers-cpp",
+    ],
+    static_libs: [
+        "libbluetooth-protos",
+    ]
+}
+
+cc_library {
+    name: "libbluetooth_gd",
+    defaults: [
+        "libbluetooth_gd_defaults",
+    ],
+}
+
+cc_library {
+    name: "libbluetooth_gd_fuzzing",
+    defaults: [
+        "libbluetooth_gd_defaults",
+    ],
+    srcs: [
+        ":BluetoothOsSources_fuzz",
+    ],
+    cflags: [
+        "-DFUZZ_TARGET",
     ],
 }
 
@@ -125,6 +188,7 @@
     name: "bluetooth_stack_with_facade",
     defaults: [
         "gd_defaults",
+        "gd_clang_coverage_bin",
     ],
     host_supported: true,
     srcs: [
@@ -135,20 +199,31 @@
         ":BluetoothFacade_hci_hal",
         ":BluetoothFacade_hci_layer",
         ":BluetoothFacade_l2cap_layer",
+        ":BluetoothFacade_neighbor",
         ":BluetoothFacade_security_layer",
+        ":BluetoothFacade_shim_layer",
     ],
     generated_headers: [
-        "BluetoothGeneratedPackets_h",
         "BluetoothFacadeGeneratedStub_h",
+        "BluetoothGeneratedDumpsysDataSchema_h",
+        "BluetoothGeneratedPackets_h",
+        // Needed here to guarantee that generated zip file is created before
+        // bluetooth_cert_tests.zip is packaged
+        "BluetoothFacadeAndCertGeneratedStub_py",
     ],
     generated_sources: [
         "BluetoothFacadeGeneratedStub_cc",
     ],
     static_libs: [
+        "breakpad_client",
+        "libbluetooth-protos",
         "libbluetooth_gd",
+        "libflatbuffers-cpp",
     ],
     shared_libs: [
+        "libbacktrace",
         "libchrome",
+        "libcrypto",
         "libgrpc++_unsecure",
         "libprotobuf-cpp-full",
     ],
@@ -158,56 +233,15 @@
                 "android.hardware.bluetooth@1.0",
                 "libhidlbase",
                 "libutils",
+                "libcutils",
             ],
         },
-    },
-    sanitize: {
-        address: true,
-    },
-}
-
-cc_binary {
-    name: "bluetooth_cert_stack",
-    defaults: [
-        "gd_defaults",
-    ],
-    host_supported: true,
-    srcs: [
-        "cert/cert_main.cc",
-        "cert/grpc_root_server.cc",
-        "cert/read_only_property_server.cc",
-        "grpc/grpc_module.cc",
-        ":BluetoothCertSource_hci_hal",
-        ":BluetoothCertSource_hci_layer",
-        ":BluetoothCertSource_l2cap_layer",
-    ],
-    generated_headers: [
-        "BluetoothGeneratedPackets_h",
-        "BluetoothCertStackGeneratedStub_h",
-    ],
-    generated_sources: [
-        "BluetoothCertStackGeneratedStub_cc",
-    ],
-    static_libs: [
-        "libbluetooth_gd",
-    ],
-    shared_libs: [
-        "libchrome",
-        "libgrpc++_unsecure",
-        "libprotobuf-cpp-full",
-    ],
-    target: {
-        android: {
-            shared_libs: [
-                "android.hardware.bluetooth@1.0",
-                "libhidlbase",
-                "libutils",
+        host: {
+            required: [
+                "root-canal",
             ],
         },
     },
-    sanitize: {
-        address: true,
-    },
 }
 
 cc_test {
@@ -232,17 +266,20 @@
         android: {
             srcs: [
                 ":BluetoothHalTestSources_hci_android_hidl",
+                ":BluetoothOsTestSources_android",
             ],
             shared_libs: [
                 "android.hardware.bluetooth@1.0",
                 "libhidlbase",
                 "libutils",
+                "libcutils",
             ],
         },
     },
     srcs: [
         "module_unittest.cc",
         "stack_manager_unittest.cc",
+        ":BluetoothAttTestSources",
         ":BluetoothCommonTestSources",
         ":BluetoothCryptoToolboxTestSources",
         ":BluetoothHciTestSources",
@@ -251,16 +288,24 @@
         ":BluetoothPacketTestSources",
         ":BluetoothSecurityTestSources",
         ":BluetoothShimTestSources",
+        ":BluetoothStorageTestSources",
     ],
     generated_headers: [
+        "BluetoothGeneratedBundlerSchema_h_bfbs",
+        "BluetoothGeneratedDumpsysDataSchema_h",
+        "BluetoothGeneratedDumpsysModuleSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
     static_libs: [
+        "libbluetooth-protos",
         "libbluetooth_gd",
+        "libc++fs",
+        "libflatbuffers-cpp",
         "libgmock",
     ],
     shared_libs: [
         "libchrome",
+        "libcrypto",
     ],
     sanitize: {
         address: true,
@@ -288,33 +333,68 @@
     },
 }
 
-cc_fuzz {
-  name: "bluetooth_gd_fuzz_test",
-  defaults: ["gd_defaults"],
-  srcs: [
-    "fuzz_test.cc",
-    ":BluetoothHciFuzzTestSources",
-    ":BluetoothL2capFuzzTestSources",
-  ],
-  static_libs: [
-    "libbluetooth_gd",
-    "libchrome",
-    "libgmock",
-    "libgtest",
-  ],
-  host_supported: true,
-  generated_headers: [
-    "BluetoothGeneratedPackets_h",
-  ],
-  target: {
-    android: {
-        shared_libs: [
-            "android.hardware.bluetooth@1.0",
-            "libhidlbase",
-            "libutils",
-        ],
+cc_defaults {
+    name: "gd_fuzz_defaults",
+    defaults: ["gd_defaults"],
+    srcs: [
+        ":BluetoothFuzzHelperSources",
+        ":BluetoothHciFuzzHelperSources",
+    ],
+    static_libs: [
+        "libbluetooth-protos",
+        "libbluetooth_gd_fuzzing",
+        "libchrome",
+        "libgmock",
+        "libgtest",
+    ],
+    host_supported: true,
+    generated_headers: [
+        "BluetoothGeneratedDumpsysDataSchema_h",
+        "BluetoothGeneratedPackets_h",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "libflatbuffers-cpp",
+    ],
+    cflags: [
+        "-DFUZZ_TARGET",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "android.hardware.bluetooth@1.0",
+                "libhidlbase",
+                "libutils",
+            ],
+        },
     },
-  },
+}
+
+cc_fuzz {
+    name: "bluetooth_gd_fuzz_test",
+    defaults: ["gd_fuzz_defaults"],
+    srcs: [
+        "fuzz_test.cc",
+        ":BluetoothHciFuzzTestSources",
+        ":BluetoothL2capFuzzTestSources",
+    ],
+}
+
+cc_fuzz {
+    name: "bluetooth_gd_hci_layer_fuzz_test",
+    defaults: ["gd_fuzz_defaults"],
+    srcs: [
+        "hci/fuzz/hci_layer_fuzz_test.cc",
+        ":BluetoothHalFuzzSources",
+    ],
+}
+
+cc_fuzz {
+    name: "bluetooth_gd_acl_manager_fuzz_test",
+    defaults: ["gd_fuzz_defaults"],
+    srcs: [
+        "hci/fuzz/acl_manager_fuzz_test.cc",
+    ],
 }
 
 cc_benchmark {
@@ -338,7 +418,7 @@
     srcs: [
         "hci/address.cc",
         "hci/class_of_device.cc",
-        ],
+    ],
 }
 
 genrule {
@@ -359,6 +439,53 @@
     ],
 }
 
+// Generates binary schema data to be bundled and source file generated
+genrule {
+    name: "BluetoothGeneratedDumpsysBinarySchema_bfbs",
+    tools: [
+        "flatc",
+    ],
+    cmd: "$(location flatc) -I system/bt/gd -b --schema -o $(genDir) $(in) ",
+    srcs: [
+        "dumpsys_data.fbs",
+        "shim/dumpsys.fbs",
+    ],
+    out: [
+        "dumpsys_data.bfbs",
+        "dumpsys.bfbs",
+    ],
+}
+
+genrule {
+    name: "BluetoothGeneratedDumpsysDataSchema_h",
+    tools: [
+        "flatc",
+    ],
+    cmd: "$(location flatc) -I system/bt/gd -o $(genDir) --cpp $(in) ",
+    srcs: [
+        "dumpsys_data.fbs",
+        "shim/dumpsys.fbs",
+    ],
+    out: [
+        "dumpsys_data_generated.h",
+        "dumpsys_generated.h",
+    ],
+}
+
+genrule {
+    name: "BluetoothGeneratedDumpsysModuleSchema_h",
+    tools: [
+            "bluetooth_flatbuffer_bundler",
+    ],
+    cmd: "$(location bluetooth_flatbuffer_bundler) -w -m bluetooth.DumpsysData -f dumpsys_module_schema_data -n bluetooth::dumpsys -g $(genDir) $(locations :BluetoothGeneratedDumpsysBinarySchema_bfbs)",
+    srcs: [
+        ":BluetoothGeneratedDumpsysBinarySchema_bfbs",
+    ],
+    out: [
+         "dumpsys_module_schema_data.h",
+    ],
+}
+
 genrule {
     name: "BluetoothGeneratedPackets_python3_cc",
     tools: [
@@ -398,10 +525,18 @@
         "facade/common.proto",
         "facade/rootservice.proto",
         "hal/facade.proto",
-        "hci/facade.proto",
+        "hci/facade/facade.proto",
+        "hci/facade/acl_manager_facade.proto",
+        "hci/facade/controller_facade.proto",
+        "hci/facade/le_acl_manager_facade.proto",
         "hci/facade/le_advertising_manager_facade.proto",
+        "hci/facade/le_initiator_address_facade.proto",
+        "hci/facade/le_scanning_manager_facade.proto",
+        "neighbor/facade/facade.proto",
         "l2cap/classic/facade.proto",
+        "l2cap/le/facade.proto",
         "security/facade.proto",
+        "shim/facade/facade.proto",
     ],
 }
 
@@ -422,14 +557,30 @@
         "facade/rootservice.pb.h",
         "hal/facade.grpc.pb.h",
         "hal/facade.pb.h",
-        "hci/facade.grpc.pb.h",
-        "hci/facade.pb.h",
+        "hci/facade/facade.grpc.pb.h",
+        "hci/facade/facade.pb.h",
+        "hci/facade/acl_manager_facade.grpc.pb.h",
+        "hci/facade/acl_manager_facade.pb.h",
+        "hci/facade/controller_facade.grpc.pb.h",
+        "hci/facade/controller_facade.pb.h",
+        "hci/facade/le_acl_manager_facade.grpc.pb.h",
+        "hci/facade/le_acl_manager_facade.pb.h",
         "hci/facade/le_advertising_manager_facade.grpc.pb.h",
         "hci/facade/le_advertising_manager_facade.pb.h",
+        "hci/facade/le_initiator_address_facade.grpc.pb.h",
+        "hci/facade/le_initiator_address_facade.pb.h",
+        "hci/facade/le_scanning_manager_facade.grpc.pb.h",
+        "hci/facade/le_scanning_manager_facade.pb.h",
         "l2cap/classic/facade.grpc.pb.h",
         "l2cap/classic/facade.pb.h",
+        "l2cap/le/facade.grpc.pb.h",
+        "l2cap/le/facade.pb.h",
+        "neighbor/facade/facade.grpc.pb.h",
+        "neighbor/facade/facade.pb.h",
         "security/facade.grpc.pb.h",
         "security/facade.pb.h",
+        "shim/facade/facade.grpc.pb.h",
+        "shim/facade/facade.pb.h",
     ],
 }
 
@@ -450,14 +601,30 @@
         "facade/rootservice.pb.cc",
         "hal/facade.grpc.pb.cc",
         "hal/facade.pb.cc",
-        "hci/facade.grpc.pb.cc",
-        "hci/facade.pb.cc",
+        "hci/facade/facade.grpc.pb.cc",
+        "hci/facade/facade.pb.cc",
+        "hci/facade/acl_manager_facade.grpc.pb.cc",
+        "hci/facade/acl_manager_facade.pb.cc",
+        "hci/facade/controller_facade.grpc.pb.cc",
+        "hci/facade/controller_facade.pb.cc",
+        "hci/facade/le_acl_manager_facade.grpc.pb.cc",
+        "hci/facade/le_acl_manager_facade.pb.cc",
         "hci/facade/le_advertising_manager_facade.grpc.pb.cc",
         "hci/facade/le_advertising_manager_facade.pb.cc",
+        "hci/facade/le_initiator_address_facade.grpc.pb.cc",
+        "hci/facade/le_initiator_address_facade.pb.cc",
+        "hci/facade/le_scanning_manager_facade.grpc.pb.cc",
+        "hci/facade/le_scanning_manager_facade.pb.cc",
         "l2cap/classic/facade.grpc.pb.cc",
         "l2cap/classic/facade.pb.cc",
+        "l2cap/le/facade.grpc.pb.cc",
+        "l2cap/le/facade.pb.cc",
+        "neighbor/facade/facade.grpc.pb.cc",
+        "neighbor/facade/facade.pb.cc",
         "security/facade.grpc.pb.cc",
         "security/facade.pb.cc",
+        "shim/facade/facade.grpc.pb.cc",
+        "shim/facade/facade.pb.cc",
     ],
 }
 
@@ -466,195 +633,112 @@
     tools: [
         "aprotoc",
         "protoc-gen-grpc-python-plugin",
+        "soong_zip",
     ],
-    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-python-plugin) $(in) --grpc_out=$(genDir) --python_out=$(genDir); " +
-        "touch $(genDir)/facade/__init__.py; " +
-        "touch $(genDir)/hal/__init__.py; " +
-        "touch $(genDir)/hal/cert/__init__.py; " +
-        "touch $(genDir)/hci/__init__.py; " +
-        "touch $(genDir)/hci/facade/__init__.py; " +
-        "touch $(genDir)/hci/cert/__init__.py; " +
-        "touch $(genDir)/l2cap/classic/__init__.py; " +
-        "touch $(genDir)/l2cap/classic/cert/__init__.py; ",
+    cmd: "mkdir -p $(genDir)/files && " +
+        "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-python-plugin) $(in) --grpc_out=$(genDir)/files --python_out=$(genDir)/files && " +
+        "mkdir -p $(genDir)/files/cert && " +
+        "touch $(genDir)/files/cert/__init__.py && " +
+        "touch $(genDir)/files/facade/__init__.py && " +
+        "touch $(genDir)/files/hal/__init__.py && " +
+        "touch $(genDir)/files/hci/__init__.py && " +
+        "touch $(genDir)/files/hci/facade/__init__.py && " +
+        "touch $(genDir)/files/l2cap/classic/__init__.py && " +
+        "touch $(genDir)/files/l2cap/le/__init__.py && " +
+        "touch $(genDir)/files/neighbor/facade/__init__.py && " +
+        "touch $(genDir)/files/security/__init__.py && " +
+        "$(location soong_zip) -C $(genDir)/files -D $(genDir)/files -o $(out)",
     srcs: [
         ":BluetoothFacadeProto",
-        ":BluetoothCertStackProto",
     ],
-    out: [
-        "cert/rootservice_pb2_grpc.py",
-        "cert/rootservice_pb2.py",
-        "facade/__init__.py",
-        "facade/common_pb2_grpc.py",
-        "facade/common_pb2.py",
-        "facade/rootservice_pb2_grpc.py",
-        "facade/rootservice_pb2.py",
-        "hal/__init__.py",
-        "hal/facade_pb2_grpc.py",
-        "hal/facade_pb2.py",
-        "hci/__init__.py",
-        "hci/facade_pb2_grpc.py",
-        "hci/facade_pb2.py",
-        "hci/facade/__init__.py",
-        "hci/facade/le_advertising_manager_facade_pb2_grpc.py",
-        "hci/facade/le_advertising_manager_facade_pb2.py",
-        "l2cap/classic/__init__.py",
-        "l2cap/classic/facade_pb2_grpc.py",
-        "l2cap/classic/facade_pb2.py",
-        "hal/cert/__init__.py",
-        "hal/cert/api_pb2_grpc.py",
-        "hal/cert/api_pb2.py",
-        "hci/cert/__init__.py",
-        "hci/cert/api_pb2_grpc.py",
-        "hci/cert/api_pb2.py",
-        "l2cap/classic/cert/__init__.py",
-        "l2cap/classic/cert/api_pb2_grpc.py",
-        "l2cap/classic/cert/api_pb2.py",
-    ],
-}
-
-filegroup {
-    name: "BluetoothCertStackProto",
-    srcs: [
-        "cert/rootservice.proto",
-        "hal/cert/api.proto",
-        "hci/cert/api.proto",
-        "l2cap/classic/cert/api.proto",
-    ],
-}
-
-genrule {
-    name: "BluetoothCertStackGeneratedStub_h",
-    tools: [
-        "aprotoc",
-        "protoc-gen-grpc-cpp-plugin",
-    ],
-    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
-    srcs: [
-        ":BluetoothCertStackProto",
-        ":BluetoothFacadeProto", // we need to use facade/common.proto
-    ],
-    out: [
-        "cert/rootservice.grpc.pb.h",
-        "cert/rootservice.pb.h",
-        "facade/common.grpc.pb.h",
-        "facade/common.pb.h",
-        "hal/cert/api.grpc.pb.h",
-        "hal/cert/api.pb.h",
-        "hci/cert/api.grpc.pb.h",
-        "hci/cert/api.pb.h",
-        "l2cap/classic/cert/api.grpc.pb.h",
-        "l2cap/classic/cert/api.pb.h",
-    ],
-}
-
-genrule {
-    name: "BluetoothCertStackGeneratedStub_cc",
-    tools: [
-        "aprotoc",
-        "protoc-gen-grpc-cpp-plugin",
-    ],
-    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
-    srcs: [
-        ":BluetoothCertStackProto",
-        ":BluetoothFacadeProto", // we need to use facade/common.proto
-    ],
-    out: [
-        "cert/rootservice.grpc.pb.cc",
-        "cert/rootservice.pb.cc",
-        "facade/common.grpc.pb.cc",
-        "facade/common.pb.cc",
-        "hal/cert/api.grpc.pb.cc",
-        "hal/cert/api.pb.cc",
-        "hci/cert/api.grpc.pb.cc",
-        "hci/cert/api.pb.cc",
-        "l2cap/classic/cert/api.grpc.pb.cc",
-        "l2cap/classic/cert/api.pb.cc",
-    ],
+    out: ["bluetooth_cert_generated_py.zip"],
 }
 
 cc_defaults {
-  name: "bluetooth_py3_native_extension_defaults",
-  include_dirs: [
-    "external/python/cpython3/Include",
-  ],
-  target: {
-      android: {
-          include_dirs: ["external/python/cpython3/android/bionic/pyconfig"],
-      },
-      android_arm: {
-          cflags: ["-DSOABI=\"cpython-38android-arm-android-bionic\""],
-          suffix: ".cpython-38android-arm-android-bionic",
-      },
-      android_arm64: {
-          cflags: ["-DSOABI=\"cpython-38android-arm64-android-bionic\""],
-          suffix: ".cpython-38android-arm64-android-bionic",
-      },
-      android_x86: {
-          cflags: ["-DSOABI=\"cpython-38android-x86-android-bionic\""],
-          suffix: ".cpython-38android-x86-android-bionic",
-      },
-      android_x86_64: {
-          cflags: ["-DSOABI=\"cpython-38android-x86_64-android-bionic\""],
-          suffix: ".cpython-38android-x86_64-android-bionic",
-      },
-      // Regenerate include dirs with android_regen.sh
-      darwin_x86_64: {
-          include_dirs: ["external/python/cpython3/android/darwin_x86_64/pyconfig"],
-          cflags: [
-              "-Wno-deprecated-declarations",
-              "-Wno-pointer-arith",
-              "-DSOABI=\"cpython-38android-x86_64-darwin\"",
-          ],
-          suffix: ".cpython-38android-x86_64-darwin",
-      },
-      linux_bionic: {
-          // NB linux_bionic is a 'host' architecture but it uses the bionic libc like 'android'
-          // targets so use the android pyconfig.
-          include_dirs: ["external/python/cpython3/android/bionic/pyconfig"],
-          cflags: ["-DSOABI=\"cpython-38android-x86_64-linux-bionic\""],
-          suffix: ".cpython-38android-x86_64-linux-bionic",
-      },
-      linux_glibc_x86: {
-          enabled: false,
-      },
-      linux_glibc_x86_64: {
-          include_dirs: ["external/python/cpython3/android/linux_x86_64/pyconfig"],
-          cflags: ["-DSOABI=\"cpython-38android-x86_64-linux-gnu\""],
-          // Commenting out the Linux suffix so that cpython-38-x86_64-linux-gnu
-          // Python 3.8 can also import the untagged .so library per PEP 3149
-          // Keep this change until Android py3-cmd can run ACTS, gRPC and can
-          // Export Python native symbols such as PyType_Type
-          // suffix: ".cpython-38android-x86_64-linux-gnu",
-      },
-      windows: {
-          enabled: false,
-      },
-  },
-  allow_undefined_symbols: true,
+    name: "bluetooth_py3_native_extension_defaults",
+    include_dirs: [
+        "external/python/cpython3/Include",
+    ],
+    target: {
+        android: {
+            include_dirs: ["external/python/cpython3/android/bionic/pyconfig"],
+        },
+        android_arm: {
+            cflags: ["-DSOABI=\"cpython-38android-arm-android-bionic\""],
+            suffix: ".cpython-38android-arm-android-bionic",
+        },
+        android_arm64: {
+            cflags: ["-DSOABI=\"cpython-38android-arm64-android-bionic\""],
+            suffix: ".cpython-38android-arm64-android-bionic",
+        },
+        android_x86: {
+            cflags: ["-DSOABI=\"cpython-38android-x86-android-bionic\""],
+            suffix: ".cpython-38android-x86-android-bionic",
+        },
+        android_x86_64: {
+            cflags: ["-DSOABI=\"cpython-38android-x86_64-android-bionic\""],
+            suffix: ".cpython-38android-x86_64-android-bionic",
+        },
+        // Regenerate include dirs with android_regen.sh
+        darwin_x86_64: {
+            include_dirs: ["external/python/cpython3/android/darwin_x86_64/pyconfig"],
+            cflags: [
+                "-Wno-deprecated-declarations",
+                "-Wno-pointer-arith",
+                "-DSOABI=\"cpython-38android-x86_64-darwin\"",
+            ],
+            suffix: ".cpython-38android-x86_64-darwin",
+        },
+        linux_bionic: {
+            // NB linux_bionic is a 'host' architecture but it uses the bionic libc like 'android'
+            // targets so use the android pyconfig.
+            include_dirs: ["external/python/cpython3/android/bionic/pyconfig"],
+            cflags: ["-DSOABI=\"cpython-38android-x86_64-linux-bionic\""],
+            suffix: ".cpython-38android-x86_64-linux-bionic",
+        },
+        linux_glibc_x86: {
+            enabled: false,
+        },
+        linux_glibc_x86_64: {
+            include_dirs: ["external/python/cpython3/android/linux_x86_64/pyconfig"],
+            cflags: ["-DSOABI=\"cpython-38android-x86_64-linux-gnu\""],
+            // Commenting out the Linux suffix so that cpython-38-x86_64-linux-gnu
+            // Python 3.8 can also import the untagged .so library per PEP 3149
+            // Keep this change until Android py3-cmd can run ACTS, gRPC and can
+            // Export Python native symbols such as PyType_Type
+            // suffix: ".cpython-38android-x86_64-linux-gnu",
+        },
+        windows: {
+            enabled: false,
+        },
+    },
+    allow_undefined_symbols: true,
 }
 
-cc_library_host_shared{
-  name: "bluetooth_packets_python3",
-  defaults: [
-    "gd_defaults",
-    "bluetooth_py3_native_extension_defaults"
-  ],
-  srcs: [
-    "packet/python3_module.cc",
-    "l2cap/fcs.cc",
-    ":BluetoothPacketSources",
-  ],
-  generated_headers: [
-    "BluetoothGeneratedPackets_h",
-  ],
-  generated_sources: [
-    "BluetoothGeneratedPackets_python3_cc",
-  ],
-  header_libs: [
-    "pybind11_headers",
-  ],
-  cflags: [
-    "-fexceptions",
-  ],
-  rtti: true,
+cc_library_host_shared {
+    name: "bluetooth_packets_python3",
+    defaults: [
+        "gd_defaults",
+        "bluetooth_py3_native_extension_defaults",
+    ],
+    srcs: [
+        "packet/python3_module.cc",
+        "l2cap/fcs.cc",
+        ":BluetoothPacketSources",
+        "hci/address.cc",
+        "hci/class_of_device.cc",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+    ],
+    generated_sources: [
+        "BluetoothGeneratedPackets_python3_cc",
+    ],
+    header_libs: [
+        "pybind11_headers",
+    ],
+    cflags: [
+        "-fexceptions",
+    ],
+    rtti: true,
 }
diff --git a/gd/Android.mk b/gd/Android.mk
new file mode 100644
index 0000000..95e644c
--- /dev/null
+++ b/gd/Android.mk
@@ -0,0 +1,100 @@
+LOCAL_PATH := $(call my-dir)
+
+LOCAL_cert_test_sources := \
+	$(call all-named-files-under,*.py,.) \
+	cert/all_cert_testcases
+LOCAL_cert_test_sources := \
+	$(filter-out gd_cert_venv% venv%, $(LOCAL_cert_test_sources))
+LOCAL_cert_test_sources := \
+	$(addprefix $(LOCAL_PATH)/, $(LOCAL_cert_test_sources))
+
+LOCAL_host_executables := \
+	$(HOST_OUT_EXECUTABLES)/bluetooth_stack_with_facade \
+	$(HOST_OUT_EXECUTABLES)/root-canal
+
+LOCAL_host_python_extension_libraries := \
+	$(HOST_OUT_SHARED_LIBRARIES)/bluetooth_packets_python3.so
+
+LOCAL_host_libraries := \
+	$(HOST_OUT_SHARED_LIBRARIES)/libbase.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libbluetooth_gd.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libc++.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libchrome.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libcrypto-host.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libevent-host.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libgrpc++_unsecure.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/liblog.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libz-host.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libprotobuf-cpp-full.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libunwindstack.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libdexfile_support.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/liblzma.so \
+	$(HOST_OUT_SHARED_LIBRARIES)/libbacktrace.so
+
+LOCAL_target_executables := \
+	$(TARGET_OUT_EXECUTABLES)/bluetooth_stack_with_facade
+
+LOCAL_target_libraries := \
+	$(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so \
+	$(TARGET_OUT_SHARED_LIBRARIES)/libbluetooth_gd.so \
+	$(TARGET_OUT_SHARED_LIBRARIES)/libgrpc++_unsecure.so
+
+bluetooth_cert_src_and_bin_zip := \
+	$(call intermediates-dir-for,PACKAGING,bluetooth_cert_src_and_bin,HOST)/bluetooth_cert_src_and_bin.zip
+
+# Assume 64-bit OS
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_cert_test_sources := $(LOCAL_cert_test_sources)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_host_executables := $(LOCAL_host_executables)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_host_python_extension_libraries := $(LOCAL_host_python_extension_libraries)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_host_libraries := $(LOCAL_host_libraries)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_target_executables := $(LOCAL_target_executables)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_target_libraries := $(LOCAL_target_libraries)
+$(bluetooth_cert_src_and_bin_zip): $(SOONG_ZIP) $(LOCAL_cert_test_sources) \
+		$(LOCAL_host_executables) $(LOCAL_host_libraries) $(LOCAL_host_python_extension_libraries) \
+		$(LOCAL_target_executables) $(LOCAL_target_libraries)
+	$(hide) $(SOONG_ZIP) -d -o $@ \
+		-C system/bt/gd $(addprefix -f ,$(PRIVATE_cert_test_sources)) \
+		-C $(HOST_OUT_EXECUTABLES) $(addprefix -f ,$(PRIVATE_host_executables)) \
+		-C $(HOST_OUT_SHARED_LIBRARIES) $(addprefix -f ,$(PRIVATE_host_python_extension_libraries)) \
+		-P lib64 \
+		-C $(HOST_OUT_SHARED_LIBRARIES) $(addprefix -f ,$(PRIVATE_host_libraries)) \
+		-P target \
+		-C $(TARGET_OUT_EXECUTABLES) $(addprefix -f ,$(PRIVATE_target_executables)) \
+		-C $(TARGET_OUT_SHARED_LIBRARIES) $(addprefix -f ,$(PRIVATE_target_libraries))
+
+# TODO: Find a better way to locate output from SOONG genrule()
+LOCAL_cert_generated_py_zip := \
+	$(SOONG_OUT_DIR)/.intermediates/system/bt/gd/BluetoothFacadeAndCertGeneratedStub_py/gen/bluetooth_cert_generated_py.zip
+
+LOCAL_acts_zip := $(HOST_OUT)/acts-dist/acts.zip
+
+bluetooth_cert_tests_py_package_zip := \
+	$(call intermediates-dir-for,PACKAGING,bluetooth_cert_tests_py_package,HOST)/bluetooth_cert_tests.zip
+
+$(bluetooth_cert_tests_py_package_zip): PRIVATE_cert_src_and_bin_zip := $(bluetooth_cert_src_and_bin_zip)
+$(bluetooth_cert_tests_py_package_zip): PRIVATE_acts_zip := $(LOCAL_acts_zip)
+$(bluetooth_cert_tests_py_package_zip): PRIVATE_cert_generated_py_zip := $(LOCAL_cert_generated_py_zip)
+$(bluetooth_cert_tests_py_package_zip): $(SOONG_ZIP) $(LOCAL_acts_zip) \
+		$(bluetooth_cert_src_and_bin_zip) $(bluetooth_cert_generated_py_zip)
+	@echo "Packaging Bluetooth Cert Tests into $@"
+	@rm -rf $(dir $@)bluetooth_cert_tests
+	@rm -rf $(dir $@)acts
+	@mkdir -p $(dir $@)bluetooth_cert_tests
+	@mkdir -p $(dir $@)acts
+	$(hide) unzip -o -q $(PRIVATE_acts_zip) "tools/test/connectivity/acts/framework/*" -d $(dir $@)acts
+	$(hide) unzip -o -q $(PRIVATE_cert_src_and_bin_zip) -d $(dir $@)bluetooth_cert_tests
+	$(hide) unzip -o -q $(PRIVATE_cert_generated_py_zip) -d $(dir $@)bluetooth_cert_tests
+	# Make all subdirectory of gd Python pacakages except lib64 and target
+	$(hide) for f in `find $(dir $@)bluetooth_cert_tests -type d -name "*" \
+					-not -path "$(dir $@)bluetooth_cert_tests/target*" \
+					-not -path "$(dir $@)bluetooth_cert_tests/lib64*"` \
+			; do (touch -a $$f/__init__.py) ; done
+	$(hide) $(SOONG_ZIP) -d -o $@ -C $(dir $@)bluetooth_cert_tests -D $(dir $@)bluetooth_cert_tests \
+		-P acts_framework \
+		-C $(dir $@)acts/tools/test/connectivity/acts/framework -D $(dir $@)acts/tools/test/connectivity/acts/framework \
+		-P llvm_binutils -C $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION) \
+		-f $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-cov \
+		-f $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-profdata \
+		-f $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib64/libc++.so.1
+
+$(call dist-for-goals,bluetooth_stack_with_facade,$(bluetooth_cert_tests_py_package_zip):bluetooth_cert_tests.zip)
\ No newline at end of file
diff --git a/gd/README.md b/gd/README.md
new file mode 100644
index 0000000..18a0211
--- /dev/null
+++ b/gd/README.md
@@ -0,0 +1,27 @@
+### Why is gabeldorsche plural?
+
+Please see this [informative video we've prepared](https://www.youtube.com/watch?v=vLRyJ0dawjM).
+
+### Architecture
+
+Guidelines for developing the Gabeldorsche (GD) stack
+
+*   [Architecture](./docs/architecture/architecture.md)
+*   [Style Guide](./docs/architecture/style_guide.md)
+
+### Testing
+
+Gabeldorsche (GD) was built with test driven development in mind. Three types of
+tests are used in ensuring Gabeldorsche stack's stability, correctness and free
+from regression.
+
+If you are verifying something is glued or hooked up correctly inside the stack,
+use a unit test.
+
+*   [GTest Unit Test](./docs/testing/gtest.md)
+
+If you are verifying correct behavior (especially interop problems) **DO NOT**
+write a unit test as this not a good use of your time. Write a [cert test](./cert_test.md) instead
+so it applies to any stack.
+
+*   [GD Certification Tests](./docs/testing/cert_test.md)
diff --git a/gd/TEST_MAPPING b/gd/TEST_MAPPING
index f0ddeaf..8769cbb 100644
--- a/gd/TEST_MAPPING
+++ b/gd/TEST_MAPPING
@@ -7,6 +7,10 @@
     {
       "name" : "bluetooth_packet_parser_test",
       "host" : true
+    },
+    {
+      "name" : "bluetooth_flatbuffer_test",
+      "host" : true
     }
   ]
 }
diff --git a/gd/att/Android.bp b/gd/att/Android.bp
new file mode 100644
index 0000000..b7af1ef
--- /dev/null
+++ b/gd/att/Android.bp
@@ -0,0 +1,12 @@
+filegroup {
+    name: "BluetoothAttSources",
+    srcs: [
+        "att_module.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothAttTestSources",
+    srcs: [
+    ],
+}
diff --git a/gd/att/att_module.cc b/gd/att/att_module.cc
new file mode 100644
index 0000000..0d22337
--- /dev/null
+++ b/gd/att/att_module.cc
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "att"
+
+#include "att/att_module.h"
+
+#include <memory>
+
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/le/l2cap_le_module.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace att {
+
+const ModuleFactory AttModule::Factory = ModuleFactory([]() { return new AttModule(); });
+
+namespace {
+void OnAttRegistrationCompleteLe(
+    l2cap::le::FixedChannelManager::RegistrationResult result,
+    std::unique_ptr<l2cap::le::FixedChannelService> le_smp_service) {
+  LOG_INFO("ATT channel registration complete");
+}
+
+void OnAttConnectionOpenLe(std::unique_ptr<l2cap::le::FixedChannel> channel) {
+  LOG_INFO("ATT conneciton opened");
+}
+}  // namespace
+
+struct AttModule::impl {
+  impl(
+      os::Handler* att_handler,
+      l2cap::le::L2capLeModule* l2cap_le_module,
+      l2cap::classic::L2capClassicModule* l2cap_classic_module)
+      : att_handler_(att_handler), l2cap_le_module_(l2cap_le_module), l2cap_classic_module_(l2cap_classic_module) {
+    // TODO: move that into a ATT manager, or other proper place
+    std::unique_ptr<bluetooth::l2cap::le::FixedChannelManager> l2cap_manager_le_(
+        l2cap_le_module_->GetFixedChannelManager());
+    l2cap_manager_le_->RegisterService(
+        bluetooth::l2cap::kLeAttributeCid,
+        common::BindOnce(&OnAttRegistrationCompleteLe),
+        common::Bind(&OnAttConnectionOpenLe),
+        att_handler_);
+  }
+
+  os::Handler* att_handler_;
+  l2cap::le::L2capLeModule* l2cap_le_module_;
+  l2cap::classic::L2capClassicModule* l2cap_classic_module_;
+};
+
+void AttModule::ListDependencies(ModuleList* list) {
+  list->add<l2cap::le::L2capLeModule>();
+  list->add<l2cap::classic::L2capClassicModule>();
+}
+
+void AttModule::Start() {
+  pimpl_ = std::make_unique<impl>(
+      GetHandler(), GetDependency<l2cap::le::L2capLeModule>(), GetDependency<l2cap::classic::L2capClassicModule>());
+}
+
+void AttModule::Stop() {
+  pimpl_.reset();
+}
+
+std::string AttModule::ToString() const {
+  return "Att Module";
+}
+
+// std::unique_ptr<AttManager> AttModule::GetAttManager() {
+//   return std::unique_ptr<AttManager>(
+//       new AttManager(pimpl_->att_handler_, &pimpl_->att_manager_impl));
+// }
+
+}  // namespace att
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/hal/cert/cert.h b/gd/att/att_module.h
similarity index 68%
rename from gd/hal/cert/cert.h
rename to gd/att/att_module.h
index 7dba43d..3501d64 100644
--- a/gd/hal/cert/cert.h
+++ b/gd/att/att_module.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,34 +13,36 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #pragma once
 
-#include <grpc++/grpc++.h>
+#include <memory>
 
-#include "grpc/grpc_module.h"
+#include "module.h"
 
 namespace bluetooth {
-namespace hal {
-namespace cert {
+namespace att {
 
-class HciHalCertService;
-
-class HalCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class AttModule : public bluetooth::Module {
  public:
+  AttModule() = default;
+  ~AttModule() = default;
+
   static const ModuleFactory Factory;
 
+ protected:
   void ListDependencies(ModuleList* list) override;
 
   void Start() override;
+
   void Stop() override;
 
-  ::grpc::Service* GetService() const override;
+  std::string ToString() const override;
 
  private:
-  HciHalCertService* service_;
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+  DISALLOW_COPY_AND_ASSIGN(AttModule);
 };
 
-}  // namespace cert
-}  // namespace hal
+}  // namespace att
 }  // namespace bluetooth
diff --git a/gd/cert/all_cert_testcases b/gd/cert/all_cert_testcases
new file mode 100644
index 0000000..d79baff
--- /dev/null
+++ b/gd/cert/all_cert_testcases
@@ -0,0 +1,17 @@
+CertSelfTest
+SimpleHalTest
+DirectHciTest
+LeAdvertisingManagerTest
+LeScanningManagerTest
+NeighborTest
+ControllerTest
+AclManagerTest
+LeAclManagerTest
+StackTest
+L2capTest
+LeL2capTest
+DualL2capTest
+LeSecurityTest
+L2capPerformanceTest
+SecurityTest
+ShimTest
diff --git a/gd/cert/android_devices_config.json b/gd/cert/android_devices_config.json
index 2bcbf9d..26c49d2 100644
--- a/gd/cert/android_devices_config.json
+++ b/gd/cert/android_devices_config.json
@@ -7,11 +7,12 @@
             "GdDevice":
             [
                 {
-                    "grpc_port": "8899",
-                    "grpc_root_server_port": "8897",
-                    "signal_port": "8895",
-                    "label": "stack_under_test",
-                    "serial_number": "DUT",
+                    "grpc_port": "8898",
+                    "grpc_root_server_port": "8896",
+                    "signal_port": "8894",
+                    "label": "cert",
+                    "serial_number": "CERT",
+                    "name": "Cert Device",
                     "cmd":
                     [
                         "adb",
@@ -24,23 +25,21 @@
                         "--btsnoop=data/misc/bluetooth/logs/btsnoop_hci.log",
                         "--signal-port=$(signal_port)"
                     ]
-                }
-            ],
-            "GdCertDevice":
-            [
+                },
                 {
-                    "grpc_port": "8898",
-                    "grpc_root_server_port": "8896",
-                    "signal_port": "8894",
-                    "label": "cert_stack",
-                    "serial_number": "CERT",
+                    "grpc_port": "8899",
+                    "grpc_root_server_port": "8897",
+                    "signal_port": "8895",
+                    "label": "dut",
+                    "serial_number": "DUT",
+                    "name": "DUT Device",
                     "cmd":
                     [
                         "adb",
                         "-s",
                         "$(serial_number)",
                         "shell",
-                        "/system/bin/bluetooth_cert_stack",
+                        "/system/bin/bluetooth_stack_with_facade",
                         "--grpc-port=$(grpc_port)",
                         "--root-server-port=$(grpc_root_server_port)",
                         "--btsnoop=data/misc/bluetooth/logs/btsnoop_hci.log",
diff --git a/gd/cert/async_subprocess_logger.py b/gd/cert/async_subprocess_logger.py
new file mode 100644
index 0000000..95300b8
--- /dev/null
+++ b/gd/cert/async_subprocess_logger.py
@@ -0,0 +1,89 @@
+#!/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 contextlib import ExitStack
+import concurrent.futures
+import logging
+import subprocess
+from cert.os_utils import TerminalColor
+
+
+class AsyncSubprocessLogger:
+    """
+    An asynchronous logger for subprocesses.Popen object's STDOUT
+
+    Contains threading functionality that allows asynchronous handling of lines
+    from STDOUT from subprocess.Popen
+    """
+    WAIT_TIMEOUT_SECONDS = 10
+    PROCESS_TAG_MIN_WIDTH = 24
+
+    def __init__(self,
+                 process: subprocess.Popen,
+                 log_file_paths,
+                 log_to_stdout=False,
+                 tag=None,
+                 color: TerminalColor = None):
+        """
+        :param process: a subprocess.Popen object with STDOUT
+        :param log_file_paths: list of log files to redirect log to
+        :param log_to_stdout: whether to dump logs to stdout in the format of
+                              "[tag] logline"
+        :param tag: tag to be used in above format
+        :param color: when dumping to stdout, what color to use for tag
+        """
+        if not process:
+            raise ValueError("process cannot be None")
+        if not process.stdout:
+            raise ValueError("process.stdout cannot be None")
+        if log_to_stdout:
+            if not tag or type(tag) is not str:
+                raise ValueError("When logging to stdout, log tag must be set")
+        self.log_file_paths = log_file_paths
+        self.log_to_stdout = log_to_stdout
+        self.tag = tag
+        self.color = color
+        self.process = process
+        self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
+        self.future = self.executor.submit(self.__logging_loop)
+
+    def stop(self):
+        """
+        Stop this logger and this object can no longer be used after this call
+        """
+        try:
+            result = self.future.result(timeout=self.WAIT_TIMEOUT_SECONDS)
+            if result:
+                logging.error("logging thread %s produced an error when executing: %s" % (self.tag, str(result)))
+        except concurrent.futures.TimeoutError:
+            logging.error("logging thread %s failed to finish after %d seconds" % (self.tag, self.WAIT_TIMEOUT_SECONDS))
+        self.executor.shutdown(wait=False)
+
+    def __logging_loop(self):
+        if self.color:
+            loggableTag = "[%s%s%s]" % (self.color, self.tag, TerminalColor.END)
+        else:
+            loggableTag = "[%s]" % self.tag
+        tagLength = len(re.sub('[^\w\s]', '', loggableTag))
+        if tagLength < self.PROCESS_TAG_MIN_WIDTH:
+            loggableTag += " " * (self.PROCESS_TAG_MIN_WIDTH - tagLength)
+        with ExitStack() as stack:
+            log_files = [stack.enter_context(open(file_path, 'w')) for file_path in self.log_file_paths]
+            for line in self.process.stdout:
+                for log_file in log_files:
+                    log_file.write(line)
+                if self.log_to_stdout:
+                    print("{}{}".format(loggableTag, line.strip()))
diff --git a/gd/cert/behavior.py b/gd/cert/behavior.py
new file mode 100644
index 0000000..8403dd6
--- /dev/null
+++ b/gd/cert/behavior.py
@@ -0,0 +1,173 @@
+#!/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 abc import ABC, abstractmethod
+from datetime import datetime, timedelta
+from mobly import signals
+from threading import Condition
+
+from cert.event_stream import static_remaining_time_delta
+from cert.truth import assertThat
+
+
+class IHasBehaviors(ABC):
+
+    @abstractmethod
+    def get_behaviors(self):
+        pass
+
+
+def anything():
+    return lambda obj: True
+
+
+def when(has_behaviors):
+    assertThat(isinstance(has_behaviors, IHasBehaviors)).isTrue()
+    return has_behaviors.get_behaviors()
+
+
+def IGNORE_UNHANDLED(obj):
+    pass
+
+
+class SingleArgumentBehavior(object):
+
+    def __init__(self, reply_stage_factory):
+        self._reply_stage_factory = reply_stage_factory
+        self._instances = []
+        self._invoked_obj = []
+        self._invoked_condition = Condition()
+        self.set_default_to_crash()
+
+    def begin(self, matcher):
+        return PersistenceStage(self, matcher, self._reply_stage_factory)
+
+    def append(self, behavior_instance):
+        self._instances.append(behavior_instance)
+
+    def set_default(self, fn):
+        assertThat(fn).isNotNone()
+        self._default_fn = fn
+
+    def set_default_to_crash(self):
+        self._default_fn = None
+
+    def set_default_to_ignore(self):
+        self._default_fn = IGNORE_UNHANDLED
+
+    def run(self, obj):
+        for instance in self._instances:
+            if instance.try_run(obj):
+                self.__obj_invoked(obj)
+                return
+        if self._default_fn is not None:
+            # IGNORE_UNHANDLED is also a default fn
+            self._default_fn(obj)
+            self.__obj_invoked(obj)
+        else:
+            raise signals.TestFailure(
+                "%s: behavior for %s went unhandled" % (self._reply_stage_factory().__class__.__name__, obj),
+                extras=None)
+
+    def __obj_invoked(self, obj):
+        self._invoked_condition.acquire()
+        self._invoked_obj.append(obj)
+        self._invoked_condition.notify()
+        self._invoked_condition.release()
+
+    def wait_until_invoked(self, matcher, times, timeout):
+        end_time = datetime.now() + timeout
+        invoked_times = 0
+        while datetime.now() < end_time and invoked_times < times:
+            remaining = static_remaining_time_delta(end_time)
+            invoked_times = sum((matcher(i) for i in self._invoked_obj))
+            self._invoked_condition.acquire()
+            self._invoked_condition.wait(remaining.total_seconds())
+            self._invoked_condition.release()
+        return invoked_times == times
+
+
+class PersistenceStage(object):
+
+    def __init__(self, behavior, matcher, reply_stage_factory):
+        self._behavior = behavior
+        self._matcher = matcher
+        self._reply_stage_factory = reply_stage_factory
+
+    def then(self, times=1):
+        reply_stage = self._reply_stage_factory()
+        reply_stage.init(self._behavior, self._matcher, times)
+        return reply_stage
+
+    def always(self):
+        return self.then(times=-1)
+
+
+class ReplyStage(object):
+
+    def init(self, behavior, matcher, persistence):
+        self._behavior = behavior
+        self._matcher = matcher
+        self._persistence = persistence
+
+    def _commit(self, fn):
+        self._behavior.append(BehaviorInstance(self._matcher, self._persistence, fn))
+
+
+class BehaviorInstance(object):
+
+    def __init__(self, matcher, persistence, fn):
+        self._matcher = matcher
+        self._persistence = persistence
+        self._fn = fn
+        self._called_count = 0
+
+    def try_run(self, obj):
+        if not self._matcher(obj):
+            return False
+        if self._persistence >= 0:
+            if self._called_count >= self._persistence:
+                return False
+        self._called_count += 1
+        self._fn(obj)
+        return True
+
+
+class BoundVerificationStage(object):
+
+    def __init__(self, behavior, matcher, timeout):
+        self._behavior = behavior
+        self._matcher = matcher
+        self._timeout = timeout
+
+    def times(self, times=1):
+        return self._behavior.wait_until_invoked(self._matcher, times, self._timeout)
+
+
+class WaitForBehaviorSubject(object):
+
+    def __init__(self, behaviors, timeout):
+        self._behaviors = behaviors
+        self._timeout = timeout
+
+    def __getattr__(self, item):
+        behavior = getattr(self._behaviors, item + "_behavior")
+        t = self._timeout
+        return lambda matcher: BoundVerificationStage(behavior, matcher, t)
+
+
+def wait_until(i_has_behaviors, timeout=timedelta(seconds=3)):
+    return WaitForBehaviorSubject(i_has_behaviors.get_behaviors(), timeout)
diff --git a/gd/cert/bluetooth_packets_python3_setup.py b/gd/cert/bluetooth_packets_python3_setup.py
index bce14f0..0eac18b 100644
--- a/gd/cert/bluetooth_packets_python3_setup.py
+++ b/gd/cert/bluetooth_packets_python3_setup.py
@@ -14,7 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-
 # Usage:
 # 1. Run envsetup and lunch first in an Android checkout
 # 2. Make target bluetooth_packets_python3 that will generate C++ sources for the
@@ -25,14 +24,12 @@
 # 4. Install:
 #       python3 bluetooth_packets_python3_setup.py install --user
 
-
 import os
 import glob
 from setuptools import setup, Extension
 
 ANDROID_BUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
-PYBIND11_INCLUDE_DIR = os.path.join(ANDROID_BUILD_TOP,
-                                    "external/python/pybind11/include")
+PYBIND11_INCLUDE_DIR = os.path.join(ANDROID_BUILD_TOP, "external/python/pybind11/include")
 GD_DIR = os.path.join(ANDROID_BUILD_TOP, "system/bt/gd")
 BT_PACKETS_GEN_DIR = os.path.join(ANDROID_BUILD_TOP,
                                   "out/soong/.intermediates/system/bt/gd/BluetoothGeneratedPackets_h/gen")
@@ -57,19 +54,17 @@
   + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "l2cap", "*.cc")) \
   + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "security", "*.cc"))
 
-bluetooth_packets_python3_module = Extension('bluetooth_packets_python3',
-                                             sources=BT_PACKETS_BASE_SRCS + BT_PACKETS_PY3_SRCs,
-                                             include_dirs=[GD_DIR,
-                                                           BT_PACKETS_GEN_DIR,
-                                                           BT_PACKETS_PY3_GEN_DIR,
-                                                           PYBIND11_INCLUDE_DIR],
-                                             extra_compile_args=['-std=c++17']
-                                             )
+bluetooth_packets_python3_module = Extension(
+    'bluetooth_packets_python3',
+    sources=BT_PACKETS_BASE_SRCS + BT_PACKETS_PY3_SRCs,
+    include_dirs=[GD_DIR, BT_PACKETS_GEN_DIR, BT_PACKETS_PY3_GEN_DIR, PYBIND11_INCLUDE_DIR],
+    extra_compile_args=['-std=c++17'])
 
-setup(name='bluetooth_packets_python3',
-      version='1.0',
-      author="Android Open Source Project",
-      description="""Bluetooth Packet Library""",
-      ext_modules=[bluetooth_packets_python3_module],
-      py_modules=["bluetooth_packets_python3"],
-      )
+setup(
+    name='bluetooth_packets_python3',
+    version='1.0',
+    author="Android Open Source Project",
+    description="""Bluetooth Packet Library""",
+    ext_modules=[bluetooth_packets_python3_module],
+    py_modules=["bluetooth_packets_python3"],
+)
diff --git a/gd/cert/capture.py b/gd/cert/capture.py
new file mode 100644
index 0000000..e37dc7d
--- /dev/null
+++ b/gd/cert/capture.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.
+
+
+class Capture(object):
+    """
+    Wrap a match function and use in its place, to capture the value
+    that matched. Specify an optional |capture_fn| to transform the
+    captured value.
+    """
+
+    def __init__(self, match_fn, capture_fn=None):
+        self._match_fn = match_fn
+        self._capture_fn = capture_fn
+        self._value = None
+
+    def __call__(self, obj):
+        if self._match_fn(obj) != True:
+            return False
+
+        if self._capture_fn is not None:
+            self._value = self._capture_fn(obj)
+        else:
+            self._value = obj
+        return True
+
+    def get(self):
+        return self._value
diff --git a/gd/cert/captures.py b/gd/cert/captures.py
new file mode 100644
index 0000000..cc50844
--- /dev/null
+++ b/gd/cert/captures.py
@@ -0,0 +1,159 @@
+#!/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 bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets
+from bluetooth_packets_python3 import l2cap_packets
+from bluetooth_packets_python3.l2cap_packets import CommandCode, LeCommandCode
+from cert.capture import Capture
+from cert.matchers import L2capMatchers
+from cert.matchers import SecurityMatchers
+from security.facade_pb2 import UiMsgType
+
+
+class HalCaptures(object):
+
+    @staticmethod
+    def ReadBdAddrCompleteCapture():
+        return Capture(
+            lambda packet: packet.payload[0:5] == b'\x0e\x0a\x01\x09\x10', lambda packet: hci_packets.ReadBdAddrCompleteView(
+                hci_packets.CommandCompleteView(
+                    hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.payload))))))
+
+    @staticmethod
+    def ConnectionRequestCapture():
+        return Capture(
+            lambda packet: packet.payload[0:2] == b'\x04\x0a', lambda packet: hci_packets.ConnectionRequestView(
+                hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.payload)))))
+
+    @staticmethod
+    def ConnectionCompleteCapture():
+        return Capture(
+            lambda packet: packet.payload[0:3] == b'\x03\x0b\x00', lambda packet: hci_packets.ConnectionCompleteView(
+                hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.payload)))))
+
+    @staticmethod
+    def DisconnectionCompleteCapture():
+        return Capture(
+            lambda packet: packet.payload[0:2] == b'\x05\x04', lambda packet: hci_packets.DisconnectionCompleteView(
+                hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.payload)))))
+
+    @staticmethod
+    def LeConnectionCompleteCapture():
+        return Capture(
+            lambda packet: packet.payload[0] == 0x3e and (packet.payload[2] == 0x01 or packet.payload[2] == 0x0a),
+            lambda packet: hci_packets.LeConnectionCompleteView(
+                hci_packets.LeMetaEventView(
+                    hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.payload))))))
+
+
+class HciCaptures(object):
+
+    @staticmethod
+    def ReadBdAddrCompleteCapture():
+        return Capture(
+            lambda packet: packet.event[0:5] == b'\x0e\x0a\x01\x09\x10', lambda packet: hci_packets.ReadBdAddrCompleteView(
+                hci_packets.CommandCompleteView(
+                    hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.event))))))
+
+    @staticmethod
+    def ConnectionRequestCapture():
+        return Capture(
+            lambda packet: packet.event[0:2] == b'\x04\x0a', lambda packet: hci_packets.ConnectionRequestView(
+                hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.event)))))
+
+    @staticmethod
+    def ConnectionCompleteCapture():
+        return Capture(
+            lambda packet: packet.event[0:3] == b'\x03\x0b\x00', lambda packet: hci_packets.ConnectionCompleteView(
+                hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.event)))))
+
+    @staticmethod
+    def DisconnectionCompleteCapture():
+        return Capture(
+            lambda packet: packet.event[0:2] == b'\x05\x04', lambda packet: hci_packets.DisconnectionCompleteView(
+                hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.event)))))
+
+    @staticmethod
+    def LeConnectionCompleteCapture():
+        return Capture(
+            lambda packet: packet.event[0] == 0x3e and (packet.event[2] == 0x01 or packet.event[2] == 0x0a),
+            lambda packet: hci_packets.LeConnectionCompleteView(
+                hci_packets.LeMetaEventView(
+                    hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet.event))))))
+
+
+class L2capCaptures(object):
+
+    @staticmethod
+    def ConnectionRequest(psm):
+        return Capture(L2capMatchers.ConnectionRequest(psm), L2capCaptures._extract_connection_request)
+
+    @staticmethod
+    def _extract_connection_request(packet):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONNECTION_REQUEST)
+        return l2cap_packets.ConnectionRequestView(frame)
+
+    @staticmethod
+    def ConnectionResponse(scid):
+        return Capture(L2capMatchers.ConnectionResponse(scid), L2capCaptures._extract_connection_response)
+
+    @staticmethod
+    def _extract_connection_response(packet):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONNECTION_RESPONSE)
+        return l2cap_packets.ConnectionResponseView(frame)
+
+    @staticmethod
+    def ConfigurationRequest(cid=None):
+        return Capture(L2capMatchers.ConfigurationRequest(cid), L2capCaptures._extract_configuration_request)
+
+    @staticmethod
+    def _extract_configuration_request(packet):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_REQUEST)
+        return l2cap_packets.ConfigurationRequestView(frame)
+
+    @staticmethod
+    def CreditBasedConnectionRequest(psm):
+        return Capture(
+            L2capMatchers.CreditBasedConnectionRequest(psm), L2capCaptures._extract_credit_based_connection_request)
+
+    @staticmethod
+    def _extract_credit_based_connection_request(packet):
+        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_REQUEST)
+        return l2cap_packets.LeCreditBasedConnectionRequestView(frame)
+
+    @staticmethod
+    def CreditBasedConnectionResponse():
+        return Capture(L2capMatchers.CreditBasedConnectionResponse(),
+                       L2capCaptures._extract_credit_based_connection_response)
+
+    @staticmethod
+    def _extract_credit_based_connection_response(packet):
+        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_RESPONSE)
+        return l2cap_packets.LeCreditBasedConnectionResponseView(frame)
+
+
+class SecurityCaptures(object):
+
+    @staticmethod
+    def DisplayPasskey():
+        return Capture(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY), SecurityCaptures._extract_passkey)
+
+    @staticmethod
+    def _extract_passkey(event):
+        if event is None:
+            return None
+        return event.numeric_value
diff --git a/gd/cert/cert_main.cc b/gd/cert/cert_main.cc
deleted file mode 100644
index 3111b70..0000000
--- a/gd/cert/cert_main.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "stack_manager.h"
-
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <csignal>
-#include <cstring>
-#include <string>
-#include <thread>
-
-#include "cert/grpc_root_server.h"
-#include "grpc/grpc_module.h"
-#include "hal/cert/cert.h"
-#include "hal/hci_hal.h"
-#include "hal/hci_hal_host_rootcanal.h"
-#include "hal/snoop_logger.h"
-
-using ::bluetooth::Module;
-using ::bluetooth::ModuleList;
-using ::bluetooth::StackManager;
-using ::bluetooth::grpc::GrpcModule;
-using ::bluetooth::os::Thread;
-
-namespace {
-::bluetooth::cert::GrpcRootServer grpc_root_server;
-
-void interrupt_handler(int) {
-  grpc_root_server.StopServer();
-}
-}  // namespace
-
-int main(int argc, const char** argv) {
-  int root_server_port = 8896;
-  int grpc_port = 8898;
-  int signal_port = 8894;
-
-  const std::string arg_grpc_root_server_port = "--root-server-port=";
-  const std::string arg_grpc_server_port = "--grpc-port=";
-  const std::string arg_rootcanal_port = "--rootcanal-port=";
-  const std::string arg_signal_port = "--signal-port=";
-  const std::string arg_btsnoop_path = "--btsnoop=";
-  std::string btsnoop_path;
-  for (int i = 1; i < argc; i++) {
-    std::string arg = argv[i];
-    if (arg.find(arg_grpc_root_server_port) == 0) {
-      auto port_number = arg.substr(arg_grpc_root_server_port.size());
-      root_server_port = std::stoi(port_number);
-    }
-    if (arg.find(arg_grpc_server_port) == 0) {
-      auto port_number = arg.substr(arg_grpc_server_port.size());
-      grpc_port = std::stoi(port_number);
-    }
-    if (arg.find(arg_rootcanal_port) == 0) {
-      auto port_number = arg.substr(arg_rootcanal_port.size());
-      ::bluetooth::hal::HciHalHostRootcanalConfig::Get()->SetPort(std::stoi(port_number));
-    }
-    if (arg.find(arg_btsnoop_path) == 0) {
-      btsnoop_path = arg.substr(arg_btsnoop_path.size());
-      ::bluetooth::hal::SnoopLogger::SetFilePath(btsnoop_path);
-    }
-    if (arg.find(arg_signal_port) == 0) {
-      auto port_number = arg.substr(arg_signal_port.size());
-      signal_port = std::stoi(port_number);
-    }
-  }
-
-  signal(SIGINT, interrupt_handler);
-  grpc_root_server.StartServer("0.0.0.0", root_server_port, grpc_port);
-  int tester_signal_socket = socket(AF_INET, SOCK_STREAM, 0);
-  struct sockaddr_in addr;
-  memset(&addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-  addr.sin_port = htons(signal_port);
-  addr.sin_addr.s_addr = htonl(INADDR_ANY);
-  connect(tester_signal_socket, (sockaddr*)&addr, sizeof(addr));
-  close(tester_signal_socket);
-  auto wait_thread = std::thread([] { grpc_root_server.RunGrpcLoop(); });
-  wait_thread.join();
-
-  return 0;
-}
diff --git a/gd/cert/cert_self_test.py b/gd/cert/cert_self_test.py
new file mode 100644
index 0000000..1555ed7
--- /dev/null
+++ b/gd/cert/cert_self_test.py
@@ -0,0 +1,670 @@
+#!/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 datetime, timedelta
+import logging
+from threading import Timer
+import time
+import traceback
+
+from mobly import asserts
+
+from acts import signals
+from acts.base_test import BaseTestClass
+
+from bluetooth_packets_python3 import hci_packets
+from bluetooth_packets_python3 import l2cap_packets
+from cert.event_stream import EventStream, FilteringEventStream
+from cert.truth import assertThat
+from cert.metadata import metadata
+from cert.behavior import when, wait_until
+from cert.behavior import IHasBehaviors
+from cert.behavior import anything
+from cert.behavior import SingleArgumentBehavior
+from cert.behavior import ReplyStage
+
+
+class BogusProto:
+
+    class BogusType:
+
+        def __init__(self):
+            self.name = "BogusProto"
+            self.is_extension = False
+            self.cpp_type = False
+
+        def type(self):
+            return 'BogusRpc'
+
+        def label(self):
+            return "label"
+
+    class BogusDescriptor:
+
+        def __init__(self, name):
+            self.full_name = name
+
+    def __init__(self, value):
+        self.value_ = value
+        self.DESCRIPTOR = BogusProto.BogusDescriptor(str(value))
+
+    def __str__(self):
+        return "BogusRpc value = " + str(self.value_)
+
+    def ListFields(self):
+        for field in [BogusProto.BogusType()]:
+            yield [field, self.value_]
+
+
+class FetchEvents:
+
+    def __init__(self, events, delay_ms):
+        self.events_ = events
+        self.sleep_time_ = (delay_ms * 1.0) / 1000
+        self.index_ = 0
+        self.done_ = False
+        self.then_ = datetime.now()
+
+    def __iter__(self):
+        for event in self.events_:
+            time.sleep(self.sleep_time_)
+            if self.done_:
+                return
+            logging.debug("yielding %d" % event)
+            yield BogusProto(event)
+
+    def done(self):
+        return self.done_
+
+    def cancel(self):
+        logging.debug("cancel")
+        self.done_ = True
+        return None
+
+
+class TestBehaviors(object):
+
+    def __init__(self, parent):
+        self.test_request_behavior = SingleArgumentBehavior(lambda: TestBehaviors.TestRequestReplyStage(parent))
+
+    def test_request(self, matcher):
+        return self.test_request_behavior.begin(matcher)
+
+    class TestRequestReplyStage(ReplyStage):
+
+        def __init__(self, parent):
+            self._parent = parent
+
+        def increment_count(self):
+            self._commit(lambda obj: self._increment_count(obj))
+            return self
+
+        def _increment_count(self, obj):
+            self._parent.count += 1
+            self._parent.captured.append(obj)
+
+
+class ObjectWithBehaviors(IHasBehaviors):
+
+    def __init__(self):
+        self.behaviors = TestBehaviors(self)
+        self.count = 0
+        self.captured = []
+        self.unhandled_count = 0
+
+    def get_behaviors(self):
+        return self.behaviors
+
+    def increment_unhandled(self):
+        self.unhandled_count += 1
+
+
+class CertSelfTest(BaseTestClass):
+
+    def setup_test(self):
+        return True
+
+    def teardown_test(self):
+        return True
+
+    def test_assert_occurs_at_least_passes(self):
+        with EventStream(FetchEvents(events=[1, 2, 3, 1, 2, 3], delay_ms=40)) as event_stream:
+            event_stream.assert_event_occurs(
+                lambda data: data.value_ == 1, timeout=timedelta(milliseconds=300), at_least_times=2)
+
+    def test_assert_occurs_passes(self):
+        with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+            event_stream.assert_event_occurs(lambda data: data.value_ == 1, timeout=timedelta(seconds=1))
+
+    def test_assert_occurs_fails(self):
+        try:
+            with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+                event_stream.assert_event_occurs(lambda data: data.value_ == 4, timeout=timedelta(seconds=1))
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_assert_occurs_at_most_passes(self):
+        with EventStream(FetchEvents(events=[1, 2, 3, 4], delay_ms=50)) as event_stream:
+            event_stream.assert_event_occurs_at_most(
+                lambda data: data.value_ < 4, timeout=timedelta(seconds=1), at_most_times=3)
+
+    def test_assert_occurs_at_most_fails(self):
+        try:
+            with EventStream(FetchEvents(events=[1, 2, 3, 4], delay_ms=50)) as event_stream:
+                event_stream.assert_event_occurs_at_most(
+                    lambda data: data.value_ > 1, timeout=timedelta(seconds=1), at_most_times=2)
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_skip_a_test(self):
+        asserts.skip("Skipping this test because it's blocked by b/xyz")
+        assert False
+
+    def test_nested_packets(self):
+        handle = 123
+        inside = hci_packets.ReadScanEnableBuilder()
+        logging.debug(inside.Serialize())
+        logging.debug("building outside")
+        outside = hci_packets.AclPacketBuilder(handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                                               hci_packets.BroadcastFlag.POINT_TO_POINT, inside)
+        logging.debug(outside.Serialize())
+        logging.debug("Done!")
+
+    def test_l2cap_config_options(self):
+        mtu_opt = l2cap_packets.MtuConfigurationOption()
+        mtu_opt.mtu = 123
+        fcs_opt = l2cap_packets.FrameCheckSequenceOption()
+        fcs_opt.fcs_type = l2cap_packets.FcsType.DEFAULT
+        request = l2cap_packets.ConfigurationRequestBuilder(
+            0x1d,  # Command ID
+            0xc1d,  # Channel ID
+            l2cap_packets.Continuation.END,
+            [mtu_opt, fcs_opt])
+        request_b_frame = l2cap_packets.BasicFrameBuilder(0x01, request)
+        handle = 123
+        wrapped = hci_packets.AclPacketBuilder(handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                                               hci_packets.BroadcastFlag.POINT_TO_POINT, request_b_frame)
+        # Size is ACL (4) + L2CAP (4) + Configure (8) + MTU (4) + FCS (3)
+        asserts.assert_true(len(wrapped.Serialize()) == 23, "Packet serialized incorrectly")
+
+    def test_assertThat_boolean_success(self):
+        assertThat(True).isTrue()
+        assertThat(False).isFalse()
+
+    def test_assertThat_boolean_falseIsTrue(self):
+        try:
+            assertThat(False).isTrue()
+        except Exception as e:
+            return True
+        return False
+
+    def test_assertThat_boolean_trueIsFalse(self):
+        try:
+            assertThat(True).isFalse()
+        except Exception as e:
+            return True
+        return False
+
+    def test_assertThat_object_success(self):
+        assertThat("this").isEqualTo("this")
+        assertThat("this").isNotEqualTo("that")
+        assertThat(None).isNone()
+        assertThat("this").isNotNone()
+
+    def test_assertThat_object_isEqualToFails(self):
+        try:
+            assertThat("this").isEqualTo("that")
+        except Exception as e:
+            return True
+        return False
+
+    def test_assertThat_object_isNotEqualToFails(self):
+        try:
+            assertThat("this").isNotEqualTo("this")
+        except Exception as e:
+            return True
+        return False
+
+    def test_assertThat_object_isNoneFails(self):
+        try:
+            assertThat("this").isNone()
+        except Exception as e:
+            return True
+        return False
+
+    def test_assertThat_object_isNotNoneFails(self):
+        try:
+            assertThat(None).isNotNone()
+        except Exception as e:
+            return True
+        return False
+
+    def test_assertThat_eventStream_emits_passes(self):
+        with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+            assertThat(event_stream).emits(lambda data: data.value_ == 1)
+
+    def test_assertThat_eventStream_emits_then_passes(self):
+        with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+            assertThat(event_stream).emits(lambda data: data.value_ == 1).then(lambda data: data.value_ == 3)
+
+    def test_assertThat_eventStream_emits_fails(self):
+        try:
+            with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+                assertThat(event_stream).emits(lambda data: data.value_ == 4)
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_assertThat_eventStream_emits_then_fails(self):
+        try:
+            with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+                assertThat(event_stream).emits(lambda data: data.value_ == 1).emits(lambda data: data.value_ == 4)
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_assertThat_eventStream_emitsInOrder_passes(self):
+        with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+            assertThat(event_stream).emits(lambda data: data.value_ == 1, lambda data: data.value_ == 2).inOrder()
+
+    def test_assertThat_eventStream_emitsInAnyOrder_passes(self):
+        with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+            assertThat(event_stream).emits(
+                lambda data: data.value_ == 2,
+                lambda data: data.value_ == 1).inAnyOrder().then(lambda data: data.value_ == 3)
+
+    def test_assertThat_eventStream_emitsInOrder_fails(self):
+        try:
+            with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+                assertThat(event_stream).emits(lambda data: data.value_ == 2, lambda data: data.value_ == 1).inOrder()
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_assertThat_eventStream_emitsInAnyOrder_fails(self):
+        try:
+            with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+                assertThat(event_stream).emits(lambda data: data.value_ == 4,
+                                               lambda data: data.value_ == 1).inAnyOrder()
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_assertThat_emitsNone_passes(self):
+        with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+            assertThat(event_stream).emitsNone(
+                lambda data: data.value_ == 4, timeout=timedelta(seconds=0.15)).thenNone(
+                    lambda data: data.value_ == 5, timeout=timedelta(seconds=0.15))
+
+    def test_assertThat_emitsNone_passes_after_1_second(self):
+        with EventStream(FetchEvents(events=[1, 2, 3, 4], delay_ms=400)) as event_stream:
+            assertThat(event_stream).emitsNone(lambda data: data.value_ == 4, timeout=timedelta(seconds=1))
+
+    def test_assertThat_emitsNone_fails(self):
+        try:
+            with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+                assertThat(event_stream).emitsNone(lambda data: data.value_ == 2, timeout=timedelta(seconds=1))
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_assertThat_emitsNone_zero_passes(self):
+        with EventStream(FetchEvents(events=[], delay_ms=50)) as event_stream:
+            assertThat(event_stream).emitsNone(timeout=timedelta(milliseconds=10)).thenNone(
+                timeout=timedelta(milliseconds=10))
+
+    def test_assertThat_emitsNone_zero_passes_after_one_second(self):
+        with EventStream(FetchEvents([1], delay_ms=1500)) as event_stream:
+            assertThat(event_stream).emitsNone(timeout=timedelta(seconds=1.0))
+
+    def test_assertThat_emitsNone_zero_fails(self):
+        try:
+            with EventStream(FetchEvents(events=[17], delay_ms=50)) as event_stream:
+                assertThat(event_stream).emitsNone(timeout=timedelta(seconds=1))
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_filtering_event_stream_none_filter_function(self):
+        with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+            filtered_event_stream = FilteringEventStream(event_stream, None)
+            assertThat(filtered_event_stream)\
+                .emits(lambda data: data.value_ == 1)\
+                .then(lambda data: data.value_ == 3)
+
+    def test_metadata_empty(self):
+
+        @metadata()
+        def simple_pass_test(arg):
+            pass
+
+        try:
+            simple_pass_test(1)
+        except signals.TestFailure:
+            pass
+        except Exception as e:
+            asserts.fail("@metadata() should only raise signals.TestFailure, "
+                         "but raised %s with msg %s instead" % (e.__class__.__name__, str(e)))
+        else:
+            asserts.fail("@metadata() should not work")
+
+    def test_metadata_empty_no_function_call(self):
+
+        @metadata
+        def simple_pass_test(arg):
+            pass
+
+        try:
+            simple_pass_test(1)
+        except signals.TestFailure:
+            pass
+        except Exception as e:
+            asserts.fail("@metadata should only raise signals.TestFailure, "
+                         "but raised %s with msg %s instead" % (e.__class__.__name__, str(e)))
+        else:
+            asserts.fail("@metadata should not work")
+
+    def test_metadata_pts_missing_id(self):
+
+        @metadata(pts_test_name="Hello world")
+        def simple_pass_test(arg):
+            pass
+
+        try:
+            simple_pass_test(1)
+        except signals.TestFailure:
+            pass
+        except Exception as e:
+            asserts.fail("should only raise signals.TestFailure, "
+                         "but raised %s with msg %s instead" % (e.__class__.__name__, str(e)))
+        else:
+            asserts.fail("missing pts_test_id should not work")
+
+    def test_metadata_pts_missing_name(self):
+
+        @metadata(pts_test_id="A/B/C")
+        def simple_pass_test(arg):
+            pass
+
+        try:
+            simple_pass_test(1)
+        except signals.TestFailure:
+            pass
+        except Exception as e:
+            asserts.fail("should only raise signals.TestFailure, "
+                         "but raised %s with msg %s instead" % (e.__class__.__name__, str(e)))
+        else:
+            asserts.fail("missing pts_test_name should not work")
+
+    def test_metadata_pts_test_id_and_description(self):
+
+        @metadata(pts_test_id="A/B/C", pts_test_name="Hello world")
+        def simple_pass_test(arg):
+            pass
+
+        try:
+            simple_pass_test(1)
+        except signals.TestPass as e:
+            asserts.assert_true("pts_test_id" in e.extras, msg=("pts_test_id not in extra: %s" % str(e.extras)))
+            asserts.assert_equal(e.extras["pts_test_id"], "A/B/C")
+            asserts.assert_true("pts_test_name" in e.extras, msg=("pts_test_name not in extra: %s" % str(e.extras)))
+            asserts.assert_equal(e.extras["pts_test_name"], "Hello world")
+        else:
+            asserts.fail("Must throw an exception using @metadata decorator")
+
+    def test_metadata_test_with_exception_stacktrace(self):
+
+        @metadata(pts_test_id="A/B/C", pts_test_name="Hello world")
+        def simple_fail_test(failure_argument):
+            raise ValueError(failure_argument)
+
+        try:
+            simple_fail_test("BEEFBEEF")
+        except signals.TestError as e:
+            asserts.assert_true("pts_test_id" in e.extras, msg=("pts_test_id not in extra: %s" % str(e.extras)))
+            asserts.assert_equal(e.extras["pts_test_id"], "A/B/C")
+            asserts.assert_true("pts_test_name" in e.extras, msg=("pts_test_name not in extra: %s" % str(e.extras)))
+            asserts.assert_equal(e.extras["pts_test_name"], "Hello world")
+            trace_str = traceback.format_exc()
+            asserts.assert_true(
+                "raise ValueError(failure_argument)" in trace_str,
+                msg="Failed test method not in error stack trace: %s" % trace_str)
+        else:
+            asserts.fail("Must throw an exception using @metadata decorator")
+
+    def test_fluent_behavior_simple(self):
+        thing = ObjectWithBehaviors()
+
+        when(thing).test_request(anything()).then().increment_count()
+
+        thing.behaviors.test_request_behavior.run("A")
+
+        assertThat(thing.count).isEqualTo(1)
+        assertThat(thing.captured).isEqualTo(["A"])
+
+    def test_fluent_behavior__then_single__captures_one(self):
+        thing = ObjectWithBehaviors()
+
+        thing.behaviors.test_request_behavior.set_default_to_ignore()
+
+        when(thing).test_request(anything()).then().increment_count()
+
+        thing.behaviors.test_request_behavior.run("A")
+        thing.behaviors.test_request_behavior.run("A")
+        thing.behaviors.test_request_behavior.run("A")
+
+        assertThat(thing.count).isEqualTo(1)
+        assertThat(thing.captured).isEqualTo(["A"])
+
+    def test_fluent_behavior__then_times__captures_all(self):
+        thing = ObjectWithBehaviors()
+
+        when(thing).test_request(anything()).then(times=3).increment_count()
+
+        thing.behaviors.test_request_behavior.run("A")
+        thing.behaviors.test_request_behavior.run("B")
+        thing.behaviors.test_request_behavior.run("C")
+
+        assertThat(thing.count).isEqualTo(3)
+        assertThat(thing.captured).isEqualTo(["A", "B", "C"])
+
+    def test_fluent_behavior__always__captures_all(self):
+        thing = ObjectWithBehaviors()
+
+        when(thing).test_request(anything()).always().increment_count()
+
+        thing.behaviors.test_request_behavior.run("A")
+        thing.behaviors.test_request_behavior.run("B")
+        thing.behaviors.test_request_behavior.run("C")
+
+        assertThat(thing.count).isEqualTo(3)
+        assertThat(thing.captured).isEqualTo(["A", "B", "C"])
+
+    def test_fluent_behavior__matcher__captures_relevant(self):
+        thing = ObjectWithBehaviors()
+        thing.behaviors.test_request_behavior.set_default_to_ignore()
+
+        when(thing).test_request(lambda obj: obj == "B").always().increment_count()
+
+        thing.behaviors.test_request_behavior.run("A")
+        thing.behaviors.test_request_behavior.run("B")
+        thing.behaviors.test_request_behavior.run("C")
+
+        assertThat(thing.count).isEqualTo(1)
+        assertThat(thing.captured).isEqualTo(["B"])
+
+    def test_fluent_behavior__then_repeated__captures_relevant(self):
+        thing = ObjectWithBehaviors()
+        thing.behaviors.test_request_behavior.set_default_to_ignore()
+
+        when(thing).test_request(anything()).then().increment_count().increment_count()
+
+        thing.behaviors.test_request_behavior.run("A")
+        thing.behaviors.test_request_behavior.run("B")
+        thing.behaviors.test_request_behavior.run("A")
+
+        assertThat(thing.count).isEqualTo(2)
+        assertThat(thing.captured).isEqualTo(["A", "B"])
+
+    def test_fluent_behavior__fallback__captures_relevant(self):
+        thing = ObjectWithBehaviors()
+        thing.behaviors.test_request_behavior.set_default_to_ignore()
+
+        when(thing).test_request(lambda obj: obj == "B").then(times=1).increment_count()
+        when(thing).test_request(lambda obj: obj == "C").always().increment_count()
+
+        thing.behaviors.test_request_behavior.run("A")
+        thing.behaviors.test_request_behavior.run("B")
+        thing.behaviors.test_request_behavior.run("A")
+        thing.behaviors.test_request_behavior.run("C")
+        thing.behaviors.test_request_behavior.run("B")
+        thing.behaviors.test_request_behavior.run("C")
+
+        assertThat(thing.count).isEqualTo(3)
+        assertThat(thing.captured).isEqualTo(["B", "C", "C"])
+
+    def test_fluent_behavior__default_unhandled_crash(self):
+        thing = ObjectWithBehaviors()
+
+        when(thing).test_request(anything()).then().increment_count()
+
+        thing.behaviors.test_request_behavior.run("A")
+        try:
+            thing.behaviors.test_request_behavior.run("A")
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_fluent_behavior__set_default_works(self):
+        thing = ObjectWithBehaviors()
+        thing.behaviors.test_request_behavior.set_default(lambda obj: thing.increment_unhandled())
+
+        when(thing).test_request(anything()).then().increment_count()
+
+        thing.behaviors.test_request_behavior.run("A")
+        thing.behaviors.test_request_behavior.run("A")
+        assertThat(thing.unhandled_count).isEqualTo(1)
+
+    def test_fluent_behavior__wait_until_done(self):
+        thing = ObjectWithBehaviors()
+        is_a = lambda obj: obj == "A"
+        when(thing).test_request(is_a).then().increment_count()
+
+        closure = lambda: thing.behaviors.test_request_behavior.run("A")
+        t = Timer(0.5, closure)
+        t.start()
+
+        wait_until(thing).test_request(is_a).times(1)
+        assertThat(thing.count).isEqualTo(1)
+        assertThat(thing.captured).isEqualTo(["A"])
+
+    def test_fluent_behavior__wait_until_done_different_lambda(self):
+        thing = ObjectWithBehaviors()
+        when(thing).test_request(lambda obj: obj == "A").then().increment_count()
+
+        closure = lambda: thing.behaviors.test_request_behavior.run("A")
+        t = Timer(0.5, closure)
+        t.start()
+
+        wait_until(thing).test_request(lambda obj: obj == "A").times(1)
+        assertThat(thing.count).isEqualTo(1)
+        assertThat(thing.captured).isEqualTo(["A"])
+
+    def test_fluent_behavior__wait_until_done_anything(self):
+        thing = ObjectWithBehaviors()
+        when(thing).test_request(lambda obj: obj == "A").then().increment_count()
+
+        closure = lambda: thing.behaviors.test_request_behavior.run("A")
+        t = Timer(0.5, closure)
+        t.start()
+
+        wait_until(thing).test_request(anything()).times(1)
+        assertThat(thing.count).isEqualTo(1)
+        assertThat(thing.captured).isEqualTo(["A"])
+
+    def test_fluent_behavior__wait_until_done_not_happened(self):
+        thing = ObjectWithBehaviors()
+        thing.behaviors.test_request_behavior.set_default_to_ignore()
+        when(thing).test_request(lambda obj: obj == "A").then().increment_count()
+
+        closure = lambda: thing.behaviors.test_request_behavior.run("B")
+        t = Timer(0.5, closure)
+        t.start()
+        assertThat(wait_until(thing).test_request(lambda obj: obj == "A").times(1)).isFalse()
+
+    def test_fluent_behavior__wait_until_done_with_default(self):
+        thing = ObjectWithBehaviors()
+        thing.behaviors.test_request_behavior.set_default(lambda obj: thing.increment_unhandled())
+
+        closure = lambda: thing.behaviors.test_request_behavior.run("A")
+        t = Timer(0.5, closure)
+        t.start()
+
+        wait_until(thing).test_request(anything()).times(1)
+        assertThat(thing.unhandled_count).isEqualTo(1)
+
+    def test_fluent_behavior__wait_until_done_two_events_AA(self):
+        thing = ObjectWithBehaviors()
+        when(thing).test_request(lambda obj: obj == "A").then().increment_count().increment_count()
+
+        closure1 = lambda: thing.behaviors.test_request_behavior.run("A")
+        t1 = Timer(0.5, closure1)
+        t1.start()
+        closure2 = lambda: thing.behaviors.test_request_behavior.run("A")
+        t2 = Timer(0.5, closure2)
+        t2.start()
+
+        wait_until(thing).test_request(lambda obj: obj == "A").times(2)
+        assertThat(thing.count).isEqualTo(2)
+        assertThat(thing.captured).isEqualTo(["A", "A"])
+
+    def test_fluent_behavior__wait_until_done_two_events_AB(self):
+        thing = ObjectWithBehaviors()
+        when(thing).test_request(anything()).always().increment_count()
+
+        closure1 = lambda: thing.behaviors.test_request_behavior.run("A")
+        t1 = Timer(0.5, closure1)
+        t1.start()
+        closure2 = lambda: thing.behaviors.test_request_behavior.run("B")
+        t2 = Timer(1, closure2)
+        t2.start()
+
+        wait_until(thing).test_request(anything()).times(2)
+        assertThat(thing.count).isEqualTo(2)
+        assertThat(thing.captured).isEqualTo(["A", "B"])
+
+    def test_fluent_behavior__wait_until_done_only_one_event_is_done(self):
+        thing = ObjectWithBehaviors()
+        when(thing).test_request(anything()).always().increment_count()
+
+        closure1 = lambda: thing.behaviors.test_request_behavior.run("A")
+        t1 = Timer(1, closure1)
+        t1.start()
+        closure2 = lambda: thing.behaviors.test_request_behavior.run("B")
+        t2 = Timer(3, closure2)
+        t2.start()
+        assertThat(wait_until(thing).test_request(lambda obj: obj == "A").times(2)).isFalse()
diff --git a/gd/cert/cert_testcases b/gd/cert/cert_testcases
deleted file mode 100644
index 2d59aa7..0000000
--- a/gd/cert/cert_testcases
+++ /dev/null
@@ -1,3 +0,0 @@
-SimpleHalTest
-SimpleHciTest
-SimpleL2capTest
\ No newline at end of file
diff --git a/gd/cert/closable.py b/gd/cert/closable.py
new file mode 100644
index 0000000..a1c0737
--- /dev/null
+++ b/gd/cert/closable.py
@@ -0,0 +1,39 @@
+#!/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 abc import ABC, abstractmethod
+
+
+class Closable(ABC):
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        self.close()
+        return traceback is None
+
+    def __del__(self):
+        self.close()
+
+    @abstractmethod
+    def close(self):
+        pass
+
+
+def safeClose(closable):
+    if closable is not None:
+        closable.close()
diff --git a/gd/cert/event_asserts.py b/gd/cert/event_asserts.py
deleted file mode 100644
index a479c55..0000000
--- a/gd/cert/event_asserts.py
+++ /dev/null
@@ -1,175 +0,0 @@
-#!/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 datetime, timedelta
-import logging
-from queue import SimpleQueue, Empty
-
-from acts import asserts
-
-from google.protobuf import text_format
-
-from cert.event_callback_stream import EventCallbackStream
-
-class EventAsserts(object):
-    """
-    A class that handles various asserts with respect to a gRPC unary stream
-
-    This class must be created before an event happens as events in a
-    EventCallbackStream is not sticky and will be lost if you don't subscribe
-    to them before generating those events.
-
-    When asserting on sequential events, a single EventAsserts object is enough
-
-    When asserting on simultaneous events, you would need multiple EventAsserts
-    objects as each EventAsserts object owns a separate queue that is actively
-    being popped as asserted events happen
-    """
-    DEFAULT_TIMEOUT_SECONDS = 3
-    DEFAULT_INCREMENTAL_TIMEOUT_SECONDS = 0.1
-
-    def __init__(self, event_callback_stream):
-        if event_callback_stream is None:
-            raise ValueError("event_callback_stream cannot be None")
-        self.event_callback_stream = event_callback_stream
-        self.event_queue = SimpleQueue()
-        self.callback = lambda event : self.event_queue.put(event)
-        self.event_callback_stream.register_callback(self.callback)
-
-    def __del__(self):
-        self.event_callback_stream.unregister_callback(self.callback)
-
-    def assert_none(self, timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
-        """
-        Assert no event happens within timeout period
-
-        :param timeout: a timedelta object
-        :return:
-        """
-        logging.debug("assert_none")
-        try:
-            event = self.event_queue.get(timeout=timeout.seconds)
-            asserts.assert_equal(event, None,
-                                 msg=(
-                                     "Expected None, but got %s" % text_format.MessageToString(
-                                     event, as_one_line=True)))
-        except Empty:
-            return
-
-    def assert_none_matching(self, match_fn,
-                             timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
-        """
-        Assert no events where match_fn(event) is True happen within timeout
-        period
-
-        :param match_fn: return True/False on match_fn(event)
-        :param timeout: a timedelta object
-        :return:
-        """
-        logging.debug("assert_none_matching")
-        event = None
-        iter_count = 0
-        timeout_seconds = timeout.seconds
-        while timeout_seconds > 0:
-            iter_count += 1
-            logging.debug("Waiting for event iteration %d" % iter_count)
-            try:
-                time_before = datetime.now()
-                current_event = self.event_queue.get(
-                    timeout=timeout_seconds)
-                time_elapsed = datetime.now() - time_before
-                timeout_seconds -= time_elapsed.seconds
-                if match_fn(current_event):
-                    event = current_event
-            except Empty:
-                continue
-        logging.debug(
-            "Done waiting for event, got %s" % text_format.MessageToString(
-                event, as_one_line=True))
-        asserts.assert_equal(event, None,
-                             msg=(
-                                 "Expected None matching, but got %s" % text_format.MessageToString(
-                                 event, as_one_line=True)))
-
-    def assert_event_occurs(self, match_fn, at_least_times=1,
-                            timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
-        """
-        Assert at least |at_least_times| instances of events happen where
-        match_fn(event) returns True within timeout period
-
-        :param match_fn: returns True/False on match_fn(event)
-        :param timeout: a timedelta object
-        :param at_least_times: how many times at least a matching event should
-                               happen
-        :return:
-        """
-        logging.debug("assert_event_occurs")
-        event = []
-        iter_count = 0
-        timeout_seconds = timeout.seconds
-        while len(event) < at_least_times and timeout_seconds > 0:
-            iter_count += 1
-            logging.debug("Waiting for event iteration %d" % iter_count)
-            try:
-                time_before = datetime.now()
-                current_event = self.event_queue.get(
-                    timeout=timeout_seconds)
-                time_elapsed = datetime.now() - time_before
-                timeout_seconds -= time_elapsed.seconds
-                if match_fn(current_event):
-                    event.append(current_event)
-            except Empty:
-                continue
-        logging.debug(
-            "Done waiting for event, got %s" % text_format.MessageToString(
-                event, as_one_line=True))
-        asserts.assert_true(len(event) == at_least_times,
-                            msg=("Expected at least %d events, but got %d" % at_least_times, len(event)))
-
-    def assert_event_occurs_at_most(self, match_fn, at_most_times,
-                                    timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
-        """
-        Assert at most |at_most_times| instances of events happen where
-        match_fn(event) returns True within timeout period
-
-        :param match_fn: returns True/False on match_fn(event)
-        :param at_most_times: how many times at most a matching event should
-                               happen
-        :param timeout:a timedelta object
-        :return:
-        """
-        logging.debug("assert_event_occurs_at_most")
-        event = []
-        iter_count = 0
-        timeout_seconds = timeout.seconds
-        while timeout_seconds > 0:
-            iter_count += 1
-            logging.debug("Waiting for event iteration %d" % iter_count)
-            try:
-                time_before = datetime.now()
-                current_event = self.event_queue.get(
-                    timeout=timeout_seconds)
-                time_elapsed = datetime.now() - time_before
-                timeout_seconds -= time_elapsed.seconds
-                if match_fn(current_event):
-                    event.append(current_event)
-            except Empty:
-                continue
-        logging.debug(
-            "Done waiting for event, got %s" % text_format.MessageToString(
-                event, as_one_line=True))
-        asserts.assert_true(len(event) <= at_most_times,
-                            msg=("Expected at most %d events, but got %d" % at_most_times, len(event)))
\ No newline at end of file
diff --git a/gd/cert/event_callback_stream.py b/gd/cert/event_callback_stream.py
deleted file mode 100644
index b739a90..0000000
--- a/gd/cert/event_callback_stream.py
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/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 concurrent.futures import ThreadPoolExecutor
-from grpc import RpcError
-from grpc._channel import _Rendezvous
-import logging
-
-
-class EventCallbackStream(object):
-    """
-    A an object that translate a gRPC stream of events to a Python stream of
-    callbacks.
-
-    All callbacks are non-sticky. This means that user will only receive callback
-    generated after EventCallbackStream is registered and will not receive any
-    callback after EventCallbackStream is unregistered
-
-    You would need a new EventCallbackStream and anything that depends on this
-    object once shutdown() is called
-    """
-
-    def __init__(self, server_stream_call):
-        """
-        Construct this object, call the |grpc_lambda| and trigger event_callback on
-        the thread used to create this object until |destroy| is called when this
-        object can no longer be used
-        :param server_stream_call: A server stream call object returned from
-                                   calling a gRPC server stream RPC API. The
-                                   object must support iterator interface (i.e.
-                                   next() method) and the grpc.Call interface
-                                   so that we can cancel it
-        :param event_callback: callback to be invoked with the only argument as
-                               the generated event. The callback will be invoked
-                               on a separate thread created within this object
-        """
-        if server_stream_call is None:
-            raise ValueError("server_stream_call must not be None")
-        self.server_stream_call = server_stream_call
-        self.handlers = []
-        self.executor = ThreadPoolExecutor()
-        self.future = self.executor.submit(EventCallbackStream._event_loop,
-                                           self)
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, type, value, traceback):
-        self.shutdown()
-        return True
-
-    def __del__(self):
-        self.shutdown()
-
-    def register_callback(self, callback, matcher_fn=None):
-        """
-        Register a callback to handle events. Event will be handled by callback
-        if matcher_fn(event) returns True
-
-        callback and matcher are registered as a tuple. Hence the same callback
-        with different matcher are considered two different handler units. Same
-        matcher, but different callback are also considered different handling
-        unit
-
-        Callback will be invoked on a ThreadPoolExecutor owned by this
-        EventCallbackStream
-
-        :param callback: Will be called as callback(event)
-        :param matcher_fn: A boolean function that returns True or False when
-                           calling matcher_fn(event), if None, all event will
-                           be matched
-        """
-        if callback is None:
-            raise ValueError("callback must not be None")
-        self.handlers.append((callback, matcher_fn))
-
-    def unregister_callback(self, callback, matcher_fn=None):
-        """
-        Unregister callback and matcher_fn from the event stream. Both objects
-        must match exactly the ones when calling register_callback()
-
-        :param callback: callback used in register_callback()
-        :param matcher_fn: matcher_fn used in register_callback()
-        :raises ValueError when (callback, matcher_fn) tuple is not found
-        """
-        if callback is None:
-            raise ValueError("callback must not be None")
-        self.handlers.remove((callback, matcher_fn))
-
-    def shutdown(self):
-        """
-        Stop the gRPC lambda so that event_callback will not be invoked after th
-        method returns.
-
-        This object will be useless after this call as there is no way to restart
-        the gRPC callback. You would have to create a new EventCallbackStream
-
-        :return: None on success, exception object on failure
-        """
-        while not self.server_stream_call.done():
-            self.server_stream_call.cancel()
-        exception_for_return = None
-        try:
-            result = self.future.result()
-            if result:
-                logging.warning("Inner loop error %s" % result)
-                raise result
-        except Exception as exp:
-            logging.warning("Exception: %s" % (exp))
-            exception_for_return = exp
-        self.executor.shutdown()
-        return exception_for_return
-
-    def _event_loop(self):
-        """
-        Main loop for consuming the gRPC stream events.
-        Blocks until computation is cancelled
-        :return: None on success, exception object on failure
-        """
-        try:
-            for event in self.server_stream_call:
-                for (callback, matcher_fn) in self.handlers:
-                    if not matcher_fn or matcher_fn(event):
-                        callback(event)
-            return None
-        except RpcError as exp:
-            if type(exp) is _Rendezvous:
-                if exp.cancelled():
-                    logging.debug("Cancelled")
-                    return None
-                else:
-                    logging.warning("Not cancelled")
-            return exp
diff --git a/gd/cert/event_stream.py b/gd/cert/event_stream.py
new file mode 100644
index 0000000..ef7729c
--- /dev/null
+++ b/gd/cert/event_stream.py
@@ -0,0 +1,294 @@
+#!/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 abc import ABC, abstractmethod
+from concurrent.futures import ThreadPoolExecutor
+from datetime import datetime, timedelta
+import logging
+from queue import SimpleQueue, Empty
+
+from mobly import asserts
+
+from google.protobuf import text_format
+
+from grpc import RpcError
+
+from cert.closable import Closable
+
+
+class IEventStream(ABC):
+
+    @abstractmethod
+    def get_event_queue(self):
+        pass
+
+
+class FilteringEventStream(IEventStream):
+
+    def __init__(self, stream, filter_fn):
+        self.filter_fn = filter_fn if filter_fn else lambda x: x
+        self.event_queue = SimpleQueue()
+        self.stream = stream
+
+        self.stream.register_callback(self.__event_callback, lambda packet: self.filter_fn(packet) is not None)
+
+    def __event_callback(self, event):
+        self.event_queue.put(self.filter_fn(event))
+
+    def get_event_queue(self):
+        return self.event_queue
+
+    def unregister(self):
+        self.stream.unregister(self.__event_callback)
+
+
+def pretty_print(proto_event):
+    return '{} {}'.format(type(proto_event).__name__, text_format.MessageToString(proto_event, as_one_line=True))
+
+
+DEFAULT_TIMEOUT_SECONDS = 3
+
+
+class EventStream(IEventStream, Closable):
+    """
+    A class that streams events from a gRPC stream, which you can assert on.
+
+    Don't use these asserts directly, use the ones from cert.truth.
+    """
+
+    def __init__(self, server_stream_call):
+        if server_stream_call is None:
+            raise ValueError("server_stream_call cannot be None")
+
+        self.server_stream_call = server_stream_call
+        self.event_queue = SimpleQueue()
+        self.handlers = []
+        self.executor = ThreadPoolExecutor()
+        self.future = self.executor.submit(EventStream.__event_loop, self)
+
+    def get_event_queue(self):
+        return self.event_queue
+
+    def close(self):
+        """
+        Stop the gRPC lambda so that event_callback will not be invoked after
+        the method returns.
+
+        This object will be useless after this call as there is no way to
+        restart the gRPC callback. You would have to create a new EventStream
+
+        :raise None on success, or the same exception as __event_loop(), or
+               concurrent.futures.TimeoutError if underlying stream failed to
+               terminate within DEFAULT_TIMEOUT_SECONDS
+        """
+        # Try to cancel the execution, don't care the result, non-blocking
+        self.server_stream_call.cancel()
+        try:
+            # cancelling gRPC stream should cause __event_loop() to quit
+            # same exception will be raised by future.result() or
+            # concurrent.futures.TimeoutError will be raised after timeout
+            self.future.result(timeout=DEFAULT_TIMEOUT_SECONDS)
+        finally:
+            # Make sure we force shutdown the executor regardless of the result
+            self.executor.shutdown(wait=False)
+
+    def register_callback(self, callback, matcher_fn=None):
+        """
+        Register a callback to handle events. Event will be handled by callback
+        if matcher_fn(event) returns True
+
+        callback and matcher are registered as a tuple. Hence the same callback
+        with different matcher are considered two different handler units. Same
+        matcher, but different callback are also considered different handling
+        unit
+
+        Callback will be invoked on a ThreadPoolExecutor owned by this
+        EventStream
+
+        :param callback: Will be called as callback(event)
+        :param matcher_fn: A boolean function that returns True or False when
+                           calling matcher_fn(event), if None, all event will
+                           be matched
+        """
+        if callback is None:
+            raise ValueError("callback must not be None")
+        self.handlers.append((callback, matcher_fn))
+
+    def unregister_callback(self, callback, matcher_fn=None):
+        """
+        Unregister callback and matcher_fn from the event stream. Both objects
+        must match exactly the ones when calling register_callback()
+
+        :param callback: callback used in register_callback()
+        :param matcher_fn: matcher_fn used in register_callback()
+        :raises ValueError when (callback, matcher_fn) tuple is not found
+        """
+        if callback is None:
+            raise ValueError("callback must not be None")
+        self.handlers.remove((callback, matcher_fn))
+
+    def __event_loop(self):
+        """
+        Main loop for consuming the gRPC stream events.
+        Blocks until computation is cancelled
+        :raise grpc.Error on failure
+        """
+        try:
+            for event in self.server_stream_call:
+                self.event_queue.put(event)
+                for (callback, matcher_fn) in self.handlers:
+                    if not matcher_fn or matcher_fn(event):
+                        callback(event)
+        except RpcError as exp:
+            # Underlying gRPC stream should run indefinitely until cancelled
+            # Hence any other reason besides CANCELLED is raised as an error
+            if self.server_stream_call.cancelled():
+                logging.debug("Cancelled")
+            else:
+                raise exp
+
+    def assert_event_occurs(self, match_fn, at_least_times=1, timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+        """
+        Assert at least |at_least_times| instances of events happen where
+        match_fn(event) returns True within timeout period
+
+        :param match_fn: returns True/False on match_fn(event)
+        :param timeout: a timedelta object
+        :param at_least_times: how many times at least a matching event should
+                               happen
+        :return:
+        """
+        NOT_FOR_YOU_assert_event_occurs(self, match_fn, at_least_times, timeout)
+
+    def assert_event_occurs_at_most(self, match_fn, at_most_times, timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+        """
+        Assert at most |at_most_times| instances of events happen where
+        match_fn(event) returns True within timeout period
+
+        :param match_fn: returns True/False on match_fn(event)
+        :param at_most_times: how many times at most a matching event should
+                               happen
+        :param timeout:a timedelta object
+        :return:
+        """
+        logging.debug("assert_event_occurs_at_most")
+        event_list = []
+        end_time = datetime.now() + timeout
+        while len(event_list) <= at_most_times and datetime.now() < end_time:
+            remaining = static_remaining_time_delta(end_time)
+            logging.debug("Waiting for event iteration (%fs remaining)" % (remaining.total_seconds()))
+            try:
+                current_event = self.event_queue.get(timeout=remaining.total_seconds())
+                if match_fn(current_event):
+                    event_list.append(current_event)
+            except Empty:
+                continue
+        logging.debug("Done waiting, got %d events" % len(event_list))
+        asserts.assert_true(
+            len(event_list) <= at_most_times,
+            msg=("Expected at most %d events, but got %d" % (at_most_times, len(event_list))))
+
+
+def static_remaining_time_delta(end_time):
+    remaining = end_time - datetime.now()
+    if remaining < timedelta(milliseconds=0):
+        remaining = timedelta(milliseconds=0)
+    return remaining
+
+
+def NOT_FOR_YOU_assert_event_occurs(istream,
+                                    match_fn,
+                                    at_least_times=1,
+                                    timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+    logging.debug("assert_event_occurs %d %fs" % (at_least_times, timeout.total_seconds()))
+    event_list = []
+    end_time = datetime.now() + timeout
+    while len(event_list) < at_least_times and datetime.now() < end_time:
+        remaining = static_remaining_time_delta(end_time)
+        logging.debug("Waiting for event (%fs remaining)" % (remaining.total_seconds()))
+        try:
+            current_event = istream.get_event_queue().get(timeout=remaining.total_seconds())
+            logging.debug("current_event: %s", current_event)
+            if match_fn(current_event):
+                event_list.append(current_event)
+        except Empty:
+            continue
+    logging.debug("Done waiting for event, received %d", len(event_list))
+    asserts.assert_true(
+        len(event_list) >= at_least_times,
+        msg=("Expected at least %d events, but got %d" % (at_least_times, len(event_list))))
+
+
+def NOT_FOR_YOU_assert_all_events_occur(istream,
+                                        match_fns,
+                                        order_matters,
+                                        timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+    logging.debug("assert_all_events_occur %fs" % timeout.total_seconds())
+    pending_matches = list(match_fns)
+    matched_order = []
+    end_time = datetime.now() + timeout
+    while len(pending_matches) > 0 and datetime.now() < end_time:
+        remaining = static_remaining_time_delta(end_time)
+        logging.debug("Waiting for event (%fs remaining)" % (remaining.total_seconds()))
+        try:
+            current_event = istream.get_event_queue().get(timeout=remaining.total_seconds())
+            for match_fn in pending_matches:
+                if match_fn(current_event):
+                    pending_matches.remove(match_fn)
+                    matched_order.append(match_fn)
+        except Empty:
+            continue
+    logging.debug("Done waiting for event")
+    asserts.assert_true(
+        len(matched_order) == len(match_fns),
+        msg=("Expected at least %d events, but got %d" % (len(match_fns), len(matched_order))))
+    if order_matters:
+        correct_order = True
+        i = 0
+        while i < len(match_fns):
+            if match_fns[i] is not matched_order[i]:
+                correct_order = False
+                break
+            i += 1
+        asserts.assert_true(correct_order, "Events not received in correct order %s %s" % (match_fns, matched_order))
+
+
+def NOT_FOR_YOU_assert_none_matching(istream, match_fn, timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+    logging.debug("assert_none_matching %fs" % (timeout.total_seconds()))
+    event = None
+    end_time = datetime.now() + timeout
+    while event is None and datetime.now() < end_time:
+        remaining = static_remaining_time_delta(end_time)
+        logging.debug("Waiting for event (%fs remaining)" % (remaining.total_seconds()))
+        try:
+            current_event = istream.get_event_queue().get(timeout=remaining.total_seconds())
+            if match_fn(current_event):
+                event = current_event
+        except Empty:
+            continue
+    logging.debug("Done waiting for an event")
+    if event is None:
+        return  # Avoid an assert in MessageToString(None, ...)
+    asserts.assert_true(event is None, msg='Expected None matching, but got {}'.format(pretty_print(event)))
+
+
+def NOT_FOR_YOU_assert_none(istream, timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+    logging.debug("assert_none %fs" % (timeout.total_seconds()))
+    try:
+        event = istream.get_event_queue().get(timeout=timeout.total_seconds())
+        asserts.assert_true(event is None, msg='Expected None, but got {}'.format(pretty_print(event)))
+    except Empty:
+        return
diff --git a/gd/cert/gd_base_test.py b/gd/cert/gd_base_test.py
index 72275fd..b69efdc 100644
--- a/gd/cert/gd_base_test.py
+++ b/gd/cert/gd_base_test.py
@@ -14,68 +14,175 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from acts.base_test import BaseTestClass
-
 import importlib
 import logging
 import os
 import signal
-import sys
 import subprocess
+import traceback
 
-ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
+from functools import wraps
+from grpc import RpcError
 
-sys.path.append(ANDROID_BUILD_TOP + '/out/soong/.intermediates/system/bt/gd/BluetoothFacadeAndCertGeneratedStub_py/gen')
+from acts import asserts, signals
+from acts.context import get_current_context
+from acts.base_test import BaseTestClass
 
-ANDROID_HOST_OUT = os.environ.get('ANDROID_HOST_OUT')
-ROOTCANAL = ANDROID_HOST_OUT + "/nativetest64/root-canal/root-canal"
+from cert.async_subprocess_logger import AsyncSubprocessLogger
+from cert.os_utils import get_gd_root
+from cert.os_utils import read_crash_snippet_and_log_tail
+from cert.os_utils import is_subprocess_alive
+from cert.os_utils import make_ports_available
+from cert.os_utils import TerminalColor
+from cert.gd_device import MOBLY_CONTROLLER_CONFIG_NAME as CONTROLLER_CONFIG_NAME
+from facade import rootservice_pb2 as facade_rootservice
+
 
 class GdBaseTestClass(BaseTestClass):
-    def __init__(self, configs):
-        BaseTestClass.__init__(self, configs)
 
-        log_path_base = configs.log_path
-        gd_devices = self.controller_configs.get("GdDevice")
-        gd_cert_devices = self.controller_configs.get("GdCertDevice")
+    SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10
 
+    def setup_class(self, dut_module, cert_module):
+        self.dut_module = dut_module
+        self.cert_module = cert_module
+        self.log_path_base = get_current_context().get_full_output_path()
+        self.verbose_mode = bool(self.user_params.get('verbose_mode', False))
+        for config in self.controller_configs[CONTROLLER_CONFIG_NAME]:
+            config['verbose_mode'] = self.verbose_mode
+
+        # Start root-canal if needed
         self.rootcanal_running = False
         if 'rootcanal' in self.controller_configs:
             self.rootcanal_running = True
-            rootcanal_logpath = os.path.join(log_path_base, 'rootcanal_logs.txt')
-            self.rootcanal_logs = open(rootcanal_logpath, 'w')
-            rootcanal_config = self.controller_configs['rootcanal']
-            rootcanal_hci_port = str(rootcanal_config.get("hci_port", "6402"))
-            self.rootcanal_process = subprocess.Popen(
-                [
-                    ROOTCANAL,
-                    str(rootcanal_config.get("test_port", "6401")),
-                    rootcanal_hci_port,
-                    str(rootcanal_config.get("link_layer_port", "6403"))
-                ],
-                cwd=ANDROID_BUILD_TOP,
-                env=os.environ.copy(),
-                stdout=self.rootcanal_logs,
-                stderr=self.rootcanal_logs
-            )
-            for gd_device in gd_devices:
-                gd_device["rootcanal_port"] = rootcanal_hci_port
-            for gd_cert_device in gd_cert_devices:
-                gd_cert_device["rootcanal_port"] = rootcanal_hci_port
+            # Get root canal binary
+            rootcanal = os.path.join(get_gd_root(), "root-canal")
+            asserts.assert_true(os.path.isfile(rootcanal), "Root canal does not exist at %s" % rootcanal)
 
-        self.register_controller(
-            importlib.import_module('cert.gd_device'),
-            builtin=True)
-        self.register_controller(
-            importlib.import_module('cert.gd_cert_device'),
-            builtin=True)
+            # Get root canal log
+            self.rootcanal_logpath = os.path.join(self.log_path_base, 'rootcanal_logs.txt')
+            # Make sure ports are available
+            rootcanal_config = self.controller_configs['rootcanal']
+            rootcanal_test_port = int(rootcanal_config.get("test_port", "6401"))
+            rootcanal_hci_port = int(rootcanal_config.get("hci_port", "6402"))
+            rootcanal_link_layer_port = int(rootcanal_config.get("link_layer_port", "6403"))
+            asserts.assert_true(
+                make_ports_available((rootcanal_test_port, rootcanal_hci_port, rootcanal_link_layer_port)),
+                "Failed to make root canal ports available")
+
+            # Start root canal process
+            rootcanal_cmd = [
+                rootcanal, str(rootcanal_test_port),
+                str(rootcanal_hci_port),
+                str(rootcanal_link_layer_port)
+            ]
+            self.log.debug("Running %s" % " ".join(rootcanal_cmd))
+            self.rootcanal_process = subprocess.Popen(
+                rootcanal_cmd,
+                cwd=get_gd_root(),
+                env=os.environ.copy(),
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+                universal_newlines=True)
+            asserts.assert_true(self.rootcanal_process, msg="Cannot start root-canal at " + str(rootcanal))
+            asserts.assert_true(
+                is_subprocess_alive(self.rootcanal_process), msg="root-canal stopped immediately after running")
+
+            self.rootcanal_logger = AsyncSubprocessLogger(
+                self.rootcanal_process, [self.rootcanal_logpath],
+                log_to_stdout=self.verbose_mode,
+                tag="rootcanal",
+                color=TerminalColor.MAGENTA)
+
+            # Modify the device config to include the correct root-canal port
+            for gd_device_config in self.controller_configs.get("GdDevice"):
+                gd_device_config["rootcanal_port"] = str(rootcanal_hci_port)
+
+        # Parse and construct GD device objects
+        self.register_controller(importlib.import_module('cert.gd_device'), builtin=True)
+        self.dut = self.gd_devices[1]
+        self.cert = self.gd_devices[0]
 
     def teardown_class(self):
         if self.rootcanal_running:
-            self.rootcanal_process.send_signal(signal.SIGINT)
-            rootcanal_return_code = self.rootcanal_process.wait()
-            self.rootcanal_logs.close()
-            if rootcanal_return_code != 0 and\
-                rootcanal_return_code != -signal.SIGINT:
-                logging.error("rootcanal stopped with code: %d" %
-                              rootcanal_return_code)
-                return False
+            stop_signal = signal.SIGINT
+            self.rootcanal_process.send_signal(stop_signal)
+            try:
+                return_code = self.rootcanal_process.wait(timeout=self.SUBPROCESS_WAIT_TIMEOUT_SECONDS)
+            except subprocess.TimeoutExpired:
+                logging.error("Failed to interrupt root canal via SIGINT, sending SIGKILL")
+                stop_signal = signal.SIGKILL
+                self.rootcanal_process.kill()
+                try:
+                    return_code = self.rootcanal_process.wait(timeout=self.SUBPROCESS_WAIT_TIMEOUT_SECONDS)
+                except subprocess.TimeoutExpired:
+                    logging.error("Failed to kill root canal")
+                    return_code = -65536
+            if return_code != 0 and return_code != -stop_signal:
+                logging.error("rootcanal stopped with code: %d" % return_code)
+            self.rootcanal_logger.stop()
+
+    def setup_test(self):
+        self.dut.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(self.dut_module),))
+        self.cert.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(self.cert_module),))
+
+        self.dut.wait_channel_ready()
+        self.cert.wait_channel_ready()
+
+    def teardown_test(self):
+        self.cert.rootservice.StopStack(facade_rootservice.StopStackRequest())
+        self.dut.rootservice.StopStack(facade_rootservice.StopStackRequest())
+
+    def __getattribute__(self, name):
+        attr = super().__getattribute__(name)
+        if not callable(attr) or not GdBaseTestClass.__is_entry_function(name):
+            return attr
+
+        @wraps(attr)
+        def __wrapped(*args, **kwargs):
+            try:
+                return attr(*args, **kwargs)
+            except RpcError as e:
+                exception_info = "".join(traceback.format_exception(e.__class__, e, e.__traceback__))
+                raise signals.TestFailure(
+                    "RpcError during test\n\nRpcError:\n\n%s\n%s" % (exception_info, self.__dump_crashes()))
+
+        return __wrapped
+
+    __ENTRY_METHODS = {"setup_class", "teardown_class", "setup_test", "teardown_test"}
+
+    @staticmethod
+    def __is_entry_function(name):
+        return name.startswith("test_") or name in GdBaseTestClass.__ENTRY_METHODS
+
+    def __dump_crashes(self):
+        """
+        :return: formatted stack traces if found, or last few lines of log
+        """
+        dut_crash, dut_log_tail = self.dut.get_crash_snippet_and_log_tail()
+        cert_crash, cert_log_tail = self.cert.get_crash_snippet_and_log_tail()
+        rootcanal_crash = None
+        rootcanal_log_tail = None
+        if self.rootcanal_running and not is_subprocess_alive(self.rootcanal_process):
+            rootcanal_crash, roocanal_log_tail = read_crash_snippet_and_log_tail(self.rootcanal_logpath)
+
+        crash_detail = ""
+        if dut_crash or cert_crash or rootcanal_crash:
+            if rootcanal_crash:
+                crash_detail += "rootcanal crashed:\n\n%s\n\n" % rootcanal_crash
+            if dut_crash:
+                crash_detail += "dut stack crashed:\n\n%s\n\n" % dut_crash
+            if cert_crash:
+                crash_detail += "cert stack crashed:\n\n%s\n\n" % cert_crash
+        else:
+            if rootcanal_log_tail:
+                crash_detail += "rootcanal log tail:\n\n%s\n\n" % rootcanal_log_tail
+            if dut_log_tail:
+                crash_detail += "dut log tail:\n\n%s\n\n" % dut_log_tail
+            if cert_log_tail:
+                crash_detail += "cert log tail:\n\n%s\n\n" % cert_log_tail
+
+        return crash_detail
diff --git a/gd/cert/gd_cert_device.py b/gd/cert/gd_cert_device.py
deleted file mode 100644
index 45f0539..0000000
--- a/gd/cert/gd_cert_device.py
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/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 gd_device_base import GdDeviceBase
-from gd_device_base import replace_vars
-
-from cert import rootservice_pb2_grpc as cert_rootservice_pb2_grpc
-from hal.cert import api_pb2_grpc as hal_cert_pb2_grpc
-from hci.cert import api_pb2_grpc as hci_cert_pb2_grpc
-from l2cap.classic.cert import api_pb2_grpc as l2cap_cert_pb2_grpc
-
-ACTS_CONTROLLER_CONFIG_NAME = "GdCertDevice"
-ACTS_CONTROLLER_REFERENCE_NAME = "gd_cert_devices"
-
-def create(configs):
-    if not configs:
-        raise GdDeviceConfigError("Configuration is empty")
-    elif not isinstance(configs, list):
-        raise GdDeviceConfigError("Configuration should be a list")
-    return get_instances_with_configs(configs)
-
-
-def destroy(devices):
-    for device in devices:
-        try:
-            device.clean_up()
-        except:
-            device.log.exception("Failed to clean up properly.")
-
-
-def get_info(devices):
-    return []
-
-
-def get_instances_with_configs(configs):
-    print(configs)
-    devices = []
-    for config in configs:
-        resolved_cmd = []
-        for entry in config["cmd"]:
-            resolved_cmd.append(replace_vars(entry, config))
-        devices.append(GdCertDevice(config["grpc_port"],
-                                    config["grpc_root_server_port"],
-                                    config["signal_port"],
-                                    resolved_cmd, config["label"]))
-    return devices
-
-class GdCertDevice(GdDeviceBase):
-    def __init__(self, grpc_port, grpc_root_server_port, signal_port, cmd, label):
-        super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd,
-                         label, ACTS_CONTROLLER_CONFIG_NAME)
-
-        # Cert stubs
-        self.rootservice = cert_rootservice_pb2_grpc.RootCertStub(self.grpc_root_server_channel)
-        self.hal = hal_cert_pb2_grpc.HciHalCertStub(self.grpc_channel)
-        self.controller_read_only_property = cert_rootservice_pb2_grpc.ReadOnlyPropertyStub(self.grpc_channel)
-        self.hci = hci_cert_pb2_grpc.AclManagerCertStub(self.grpc_channel)
-        self.l2cap = l2cap_cert_pb2_grpc.L2capClassicModuleCertStub(self.grpc_channel)
diff --git a/gd/cert/gd_device.py b/gd/cert/gd_device.py
index a02c338..7f5175f 100644
--- a/gd/cert/gd_device.py
+++ b/gd/cert/gd_device.py
@@ -14,32 +14,69 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from gd_device_base import GdDeviceBase
-from gd_device_base import replace_vars
+from abc import ABC
+from datetime import datetime
+import inspect
+import logging
+import os
+import pathlib
+import shutil
+import signal
+import socket
+import subprocess
+import time
+from typing import List
 
+import grpc
+
+from acts import asserts
+from acts import utils
+from acts.context import get_current_context
+from acts.controllers.adb import AdbProxy
+from acts.controllers.adb import AdbError
+
+from google.protobuf import empty_pb2 as empty_proto
+
+from cert.async_subprocess_logger import AsyncSubprocessLogger
+from cert.logging_client_interceptor import LoggingClientInterceptor
+from cert.os_utils import get_gd_root
+from cert.os_utils import read_crash_snippet_and_log_tail
+from cert.os_utils import is_subprocess_alive
+from cert.os_utils import make_ports_available
+from cert.os_utils import TerminalColor
 from facade import rootservice_pb2_grpc as facade_rootservice_pb2_grpc
 from hal import facade_pb2_grpc as hal_facade_pb2_grpc
-from hci import facade_pb2_grpc as hci_facade_pb2_grpc
+from hci.facade import facade_pb2_grpc as hci_facade_pb2_grpc
+from hci.facade import acl_manager_facade_pb2_grpc
+from hci.facade import controller_facade_pb2_grpc
+from hci.facade import le_acl_manager_facade_pb2_grpc
 from hci.facade import le_advertising_manager_facade_pb2_grpc
+from hci.facade import le_initiator_address_facade_pb2_grpc
+from hci.facade import le_scanning_manager_facade_pb2_grpc
 from l2cap.classic import facade_pb2_grpc as l2cap_facade_pb2_grpc
+from l2cap.le import facade_pb2_grpc as l2cap_le_facade_pb2_grpc
+from neighbor.facade import facade_pb2_grpc as neighbor_facade_pb2_grpc
+from security import facade_pb2_grpc as security_facade_pb2_grpc
+from shim.facade import facade_pb2_grpc as shim_facade_pb2_grpc
 
-ACTS_CONTROLLER_CONFIG_NAME = "GdDevice"
+MOBLY_CONTROLLER_CONFIG_NAME = "GdDevice"
 ACTS_CONTROLLER_REFERENCE_NAME = "gd_devices"
 
+
 def create(configs):
     if not configs:
-        raise GdDeviceConfigError("Configuration is empty")
+        raise Exception("Configuration is empty")
     elif not isinstance(configs, list):
-        raise GdDeviceConfigError("Configuration should be a list")
+        raise Exception("Configuration should be a list")
     return get_instances_with_configs(configs)
 
 
 def destroy(devices):
     for device in devices:
         try:
-            device.clean_up()
+            device.teardown()
         except:
-            device.log.exception("Failed to clean up properly.")
+            logging.exception("[%s] Failed to clean up properly due to" % device.label)
 
 
 def get_info(devices):
@@ -51,25 +88,543 @@
     devices = []
     for config in configs:
         resolved_cmd = []
-        for entry in config["cmd"]:
-            resolved_cmd.append(replace_vars(entry, config))
-        devices.append(GdDevice(config["grpc_port"],
-                                config["grpc_root_server_port"],
-                                config["signal_port"],
-                                resolved_cmd, config["label"]))
+        for arg in config["cmd"]:
+            logging.debug(arg)
+            resolved_cmd.append(replace_vars(arg, config))
+        verbose_mode = bool(config.get('verbose_mode', False))
+        if config.get("serial_number"):
+            device = GdAndroidDevice(config["grpc_port"], config["grpc_root_server_port"], config["signal_port"],
+                                     resolved_cmd, config["label"], MOBLY_CONTROLLER_CONFIG_NAME, config["name"],
+                                     config["serial_number"], verbose_mode)
+        else:
+            device = GdHostOnlyDevice(config["grpc_port"], config["grpc_root_server_port"], config["signal_port"],
+                                      resolved_cmd, config["label"], MOBLY_CONTROLLER_CONFIG_NAME, config["name"],
+                                      verbose_mode)
+        device.setup()
+        devices.append(device)
     return devices
 
-class GdDevice(GdDeviceBase):
-    def __init__(self, grpc_port, grpc_root_server_port, signal_port, cmd, label):
-        super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd,
-                         label, ACTS_CONTROLLER_CONFIG_NAME)
 
-        # Facade stubs
+def replace_vars(string, config):
+    serial_number = config.get("serial_number")
+    if serial_number is None:
+        serial_number = ""
+    rootcanal_port = config.get("rootcanal_port")
+    if rootcanal_port is None:
+        rootcanal_port = ""
+    if serial_number == "DUT" or serial_number == "CERT":
+        raise Exception("Did you forget to configure the serial number?")
+    return string.replace("$GD_ROOT", get_gd_root()) \
+                 .replace("$(grpc_port)", config.get("grpc_port")) \
+                 .replace("$(grpc_root_server_port)", config.get("grpc_root_server_port")) \
+                 .replace("$(rootcanal_port)", rootcanal_port) \
+                 .replace("$(signal_port)", config.get("signal_port")) \
+                 .replace("$(serial_number)", serial_number)
+
+
+class GdDeviceBase(ABC):
+    """
+    Base GD device class that covers common traits which assumes that the
+    device must be driven by a driver-like backing process that takes following
+    command line arguments:
+    --grpc-port: main entry port for facade services
+    --root-server-port: management port for starting and stopping services
+    --btsnoop: path to btsnoop HCI log
+    --signal-port: signaling port to indicate that backing process is started
+    --rootcanal-port: root-canal HCI port, optional
+    """
+
+    WAIT_CHANNEL_READY_TIMEOUT_SECONDS = 10
+
+    def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str,
+                 type_identifier: str, name: str, verbose_mode: bool):
+        """Base GD device, common traits for both device based and host only GD
+        cert tests
+        :param grpc_port: main gRPC service port
+        :param grpc_root_server_port: gRPC root server port
+        :param signal_port: signaling port for backing process start up
+        :param cmd: list of arguments to run in backing process
+        :param label: device label used in logs
+        :param type_identifier: device type identifier used in logs
+        :param name: name of device used in logs
+        """
+        # Must be at the first line of __init__ method
+        values = locals()
+        arguments = [values[arg] for arg in inspect.getfullargspec(GdDeviceBase.__init__).args if arg != "verbose_mode"]
+        asserts.assert_true(all(arguments), "All arguments to GdDeviceBase must not be None nor empty")
+        asserts.assert_true(all(cmd), "cmd list should not have None nor empty component")
+        self.verbose_mode = verbose_mode
+        self.grpc_root_server_port = int(grpc_root_server_port)
+        self.grpc_port = int(grpc_port)
+        self.signal_port = int(signal_port)
+        self.name = name
+        self.type_identifier = type_identifier
+        self.label = label
+        # logging.log_path only exists when this is used in an ACTS test run.
+        self.log_path_base = get_current_context().get_full_output_path()
+        self.test_runner_base_path = \
+            get_current_context().get_base_output_path()
+        self.backing_process_log_path = os.path.join(self.log_path_base,
+                                                     '%s_%s_backing_logs.txt' % (self.type_identifier, self.label))
+        if "--btsnoop=" not in " ".join(cmd):
+            cmd.append("--btsnoop=%s" % os.path.join(self.log_path_base, '%s_btsnoop_hci.log' % self.label))
+        self.cmd = cmd
+        self.environment = os.environ.copy()
+        if "cert" in self.label:
+            self.terminal_color = TerminalColor.BLUE
+        else:
+            self.terminal_color = TerminalColor.YELLOW
+
+    def setup(self):
+        """Set up this device for test, must run before using this device
+        - After calling this, teardown() must be called when test finishes
+        - Should be executed after children classes' setup() methods
+        :return:
+        """
+        # Ensure signal port is available
+        # signal port is the only port that always listen on the host machine
+        asserts.assert_true(
+            make_ports_available([self.signal_port]), "[%s] Failed to make signal port available" % self.label)
+        # Start backing process
+        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as signal_socket:
+            # Setup signaling socket
+            signal_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+            signal_socket.bind(("localhost", self.signal_port))
+            signal_socket.listen(1)
+
+            # Start backing process
+            logging.debug("Running %s" % " ".join(self.cmd))
+            self.backing_process = subprocess.Popen(
+                self.cmd,
+                cwd=get_gd_root(),
+                env=self.environment,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+                universal_newlines=True)
+            asserts.assert_true(self.backing_process, msg="Cannot start backing_process at " + " ".join(self.cmd))
+            asserts.assert_true(
+                is_subprocess_alive(self.backing_process),
+                msg="backing_process stopped immediately after running " + " ".join(self.cmd))
+
+            # Wait for process to be ready
+            signal_socket.accept()
+
+        self.backing_process_logger = AsyncSubprocessLogger(
+            self.backing_process, [self.backing_process_log_path],
+            log_to_stdout=self.verbose_mode,
+            tag=self.label,
+            color=self.terminal_color)
+
+        # Setup gRPC management channels
+        self.grpc_root_server_channel = grpc.insecure_channel("localhost:%d" % self.grpc_root_server_port)
+        self.grpc_channel = grpc.insecure_channel("localhost:%d" % self.grpc_port)
+
+        if self.verbose_mode:
+            self.grpc_channel = grpc.intercept_channel(self.grpc_channel, LoggingClientInterceptor(self.label))
+
+        # Establish services from facades
         self.rootservice = facade_rootservice_pb2_grpc.RootFacadeStub(self.grpc_root_server_channel)
         self.hal = hal_facade_pb2_grpc.HciHalFacadeStub(self.grpc_channel)
         self.controller_read_only_property = facade_rootservice_pb2_grpc.ReadOnlyPropertyStub(self.grpc_channel)
-        self.hci = hci_facade_pb2_grpc.AclManagerFacadeStub(self.grpc_channel)
-        self.hci_classic_security = hci_facade_pb2_grpc.ClassicSecurityManagerFacadeStub(self.grpc_channel)
+        self.hci = hci_facade_pb2_grpc.HciLayerFacadeStub(self.grpc_channel)
         self.l2cap = l2cap_facade_pb2_grpc.L2capClassicModuleFacadeStub(self.grpc_channel)
-        self.hci_le_advertising_manager = le_advertising_manager_facade_pb2_grpc.LeAdvertisingManagerFacadeStub(self.grpc_channel)
+        self.l2cap_le = l2cap_le_facade_pb2_grpc.L2capLeModuleFacadeStub(self.grpc_channel)
+        self.hci_acl_manager = acl_manager_facade_pb2_grpc.AclManagerFacadeStub(self.grpc_channel)
+        self.hci_le_acl_manager = le_acl_manager_facade_pb2_grpc.LeAclManagerFacadeStub(self.grpc_channel)
+        self.hci_le_initiator_address = le_initiator_address_facade_pb2_grpc.LeInitiatorAddressFacadeStub(
+            self.grpc_channel)
+        self.hci_controller = controller_facade_pb2_grpc.ControllerFacadeStub(self.grpc_channel)
+        self.hci_controller.GetMacAddressSimple = lambda: self.hci_controller.GetMacAddress(empty_proto.Empty()).address
+        self.hci_controller.GetLocalNameSimple = lambda: self.hci_controller.GetLocalName(empty_proto.Empty()).name
+        self.hci_le_advertising_manager = le_advertising_manager_facade_pb2_grpc.LeAdvertisingManagerFacadeStub(
+            self.grpc_channel)
+        self.hci_le_scanning_manager = le_scanning_manager_facade_pb2_grpc.LeScanningManagerFacadeStub(
+            self.grpc_channel)
+        self.neighbor = neighbor_facade_pb2_grpc.NeighborFacadeStub(self.grpc_channel)
+        self.security = security_facade_pb2_grpc.SecurityModuleFacadeStub(self.grpc_channel)
+        self.shim = shim_facade_pb2_grpc.ShimFacadeStub(self.grpc_channel)
 
+    def get_crash_snippet_and_log_tail(self):
+        if is_subprocess_alive(self.backing_process):
+            return None, None
+
+        return read_crash_snippet_and_log_tail(self.backing_process_log_path)
+
+    def teardown(self):
+        """Tear down this device and clean up any resources.
+        - Must be called after setup()
+        - Should be executed before children classes' teardown()
+        :return:
+        """
+        self.grpc_channel.close()
+        self.grpc_root_server_channel.close()
+        stop_signal = signal.SIGINT
+        self.backing_process.send_signal(stop_signal)
+        try:
+            return_code = self.backing_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS)
+        except subprocess.TimeoutExpired:
+            logging.error("[%s] Failed to interrupt backing process via SIGINT, sending SIGKILL" % self.label)
+            stop_signal = signal.SIGKILL
+            self.backing_process.kill()
+            try:
+                return_code = self.backing_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS)
+            except subprocess.TimeoutExpired:
+                logging.error("Failed to kill backing process")
+                return_code = -65536
+        if return_code not in [-stop_signal, 0]:
+            logging.error("backing process %s stopped with code: %d" % (self.label, return_code))
+        self.backing_process_logger.stop()
+
+    def wait_channel_ready(self):
+        future = grpc.channel_ready_future(self.grpc_channel)
+        try:
+            future.result(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS)
+        except grpc.FutureTimeoutError:
+            asserts.fail("[%s] wait channel ready timeout" % self.label)
+
+
+class GdHostOnlyDevice(GdDeviceBase):
+    """
+    Host only device where the backing process is running on the host machine
+    """
+
+    def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str,
+                 type_identifier: str, name: str, verbose_mode: bool):
+        super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd, label, MOBLY_CONTROLLER_CONFIG_NAME, name,
+                         verbose_mode)
+        # Enable LLVM code coverage output for host only tests
+        self.backing_process_profraw_path = pathlib.Path(self.log_path_base).joinpath(
+            "%s_%s_backing_coverage.profraw" % (self.type_identifier, self.label))
+        self.environment["LLVM_PROFILE_FILE"] = str(self.backing_process_profraw_path)
+
+    def teardown(self):
+        super().teardown()
+        self.generate_coverage_report()
+
+    def generate_coverage_report(self):
+        if not self.backing_process_profraw_path.is_file():
+            logging.info("[%s] Skip coverage report as there is no profraw file at %s" %
+                         (self.label, str(self.backing_process_profraw_path)))
+            return
+        try:
+            if self.backing_process_profraw_path.stat().st_size <= 0:
+                logging.info("[%s] Skip coverage report as profraw file is empty at %s" %
+                             (self.label, str(self.backing_process_profraw_path)))
+                return
+        except OSError:
+            logging.info("[%s] Skip coverage report as profraw file is inaccessible at %s" %
+                         (self.label, str(self.backing_process_profraw_path)))
+            return
+        llvm_binutils = pathlib.Path(get_gd_root()).joinpath("llvm_binutils").joinpath("bin")
+        llvm_profdata = llvm_binutils.joinpath("llvm-profdata")
+        if not llvm_profdata.is_file():
+            logging.info(
+                "[%s] Skip coverage report as llvm-profdata is not found at %s" % (self.label, str(llvm_profdata)))
+            return
+        llvm_cov = llvm_binutils.joinpath("llvm-cov")
+        if not llvm_cov.is_file():
+            logging.info("[%s] Skip coverage report as llvm-cov is not found at %s" % (self.label, str(llvm_cov)))
+            return
+        logging.info("[%s] Generating coverage report" % self.label)
+        profdata_path = pathlib.Path(self.test_runner_base_path).joinpath(
+            "%s_%s_backing_process_coverage.profdata" % (self.type_identifier, self.label))
+        profdata_path_tmp = pathlib.Path(self.test_runner_base_path).joinpath(
+            "%s_%s_backing_process_coverage_tmp.profdata" % (self.type_identifier, self.label))
+        # Merge with existing profdata if possible
+        profdata_cmd = [str(llvm_profdata), "merge", "-sparse", str(self.backing_process_profraw_path)]
+        if profdata_path.is_file():
+            profdata_cmd.append(str(profdata_path))
+        profdata_cmd += ["-o", str(profdata_path_tmp)]
+        result = subprocess.run(profdata_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        if result.returncode != 0:
+            logging.warning("[%s] Failed to index profdata, cmd result: %r" % (self.label, result))
+            profdata_path.unlink(missing_ok=True)
+            return
+        shutil.move(profdata_path_tmp, profdata_path)
+        coverage_result_path = pathlib.Path(self.test_runner_base_path).joinpath(
+            "%s_%s_backing_process_coverage.json" % (self.type_identifier, self.label))
+        with coverage_result_path.open("w") as coverage_result_file:
+            result = subprocess.run(
+                [str(llvm_cov), "export", "--format=text", "--instr-profile", profdata_path, self.cmd[0]],
+                stderr=subprocess.PIPE,
+                stdout=coverage_result_file,
+                cwd=os.path.join(get_gd_root()))
+        if result.returncode != 0:
+            logging.warning("[%s] Failed to generated coverage report, cmd result: %r" % (self.label, result))
+            coverage_result_path.unlink(missing_ok=True)
+            return
+        coverage_summary_path = pathlib.Path(self.test_runner_base_path).joinpath(
+            "%s_%s_backing_process_coverage_summary.txt" % (self.type_identifier, self.label))
+        with coverage_summary_path.open("w") as coverage_summary_file:
+            result = subprocess.run(
+                [llvm_cov, "report", "--instr-profile", profdata_path, self.cmd[0]],
+                stderr=subprocess.PIPE,
+                stdout=coverage_summary_file,
+                cwd=os.path.join(get_gd_root()))
+        if result.returncode != 0:
+            logging.warning("[%s] Failed to generated coverage summary, cmd result: %r" % (self.label, result))
+            coverage_summary_path.unlink(missing_ok=True)
+
+    def setup(self):
+        # Ensure ports are available
+        # Only check on host only test, for Android devices, these ports will
+        # be opened on Android device and host machine ports will be occupied
+        # by sshd or adb forwarding
+        asserts.assert_true(
+            make_ports_available((self.grpc_port, self.grpc_root_server_port)),
+            "[%s] Failed to make backing process ports available" % self.label)
+        super().setup()
+
+
+class GdAndroidDevice(GdDeviceBase):
+    """Real Android device where the backing process is running on it
+    """
+
+    WAIT_FOR_DEVICE_TIMEOUT_SECONDS = 180
+
+    def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str,
+                 type_identifier: str, name: str, serial_number: str, verbose_mode: bool):
+        super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd, label, type_identifier, name, verbose_mode)
+        asserts.assert_true(serial_number, "serial_number must not be None nor empty")
+        self.serial_number = serial_number
+        self.adb = AdbProxy(serial_number)
+
+    def setup(self):
+        logging.info("Setting up device %s %s" % (self.label, self.serial_number))
+        asserts.assert_true(self.adb.ensure_root(), "device %s cannot run as root", self.serial_number)
+
+        # Try freeing ports and ignore results
+        self.cleanup_port_forwarding()
+        self.sync_device_time()
+
+        # Set up port forwarding or reverse or die
+        self.tcp_forward_or_die(self.grpc_port, self.grpc_port)
+        self.tcp_forward_or_die(self.grpc_root_server_port, self.grpc_root_server_port)
+        self.tcp_reverse_or_die(self.signal_port, self.signal_port)
+
+        # Push test binaries
+        self.ensure_verity_disabled()
+        self.push_or_die(os.path.join(get_gd_root(), "target", "bluetooth_stack_with_facade"), "system/bin")
+        self.push_or_die(os.path.join(get_gd_root(), "target", "libbluetooth_gd.so"), "system/lib64")
+        self.push_or_die(os.path.join(get_gd_root(), "target", "libgrpc++_unsecure.so"), "system/lib64")
+        self.adb.shell("rm /data/misc/bluetooth/logs/btsnoop_hci.log")
+        self.ensure_no_output(self.adb.shell("svc bluetooth disable"))
+
+        # Start logcat logging
+        self.logcat_output_path = os.path.join(
+            self.log_path_base, '%s_%s_%s_logcat_logs.txt' % (self.type_identifier, self.label, self.serial_number))
+        self.logcat_cmd = ["adb", "-s", self.serial_number, "logcat", "-T", "1", "-v", "year", "-v", "uid"]
+        logging.debug("Running %s", " ".join(self.logcat_cmd))
+        self.logcat_process = subprocess.Popen(
+            self.logcat_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
+        asserts.assert_true(self.logcat_process, msg="Cannot start logcat_process at " + " ".join(self.logcat_cmd))
+        asserts.assert_true(
+            is_subprocess_alive(self.logcat_process),
+            msg="logcat_process stopped immediately after running " + " ".join(self.logcat_cmd))
+        self.logcat_logger = AsyncSubprocessLogger(
+            self.logcat_process, [self.logcat_output_path],
+            log_to_stdout=self.verbose_mode,
+            tag="%s_%s" % (self.label, self.serial_number),
+            color=self.terminal_color)
+
+        # Done run parent setup
+        logging.info("Done preparation for %s, starting backing process" % self.serial_number)
+        super().setup()
+
+    def teardown(self):
+        super().teardown()
+        stop_signal = signal.SIGINT
+        self.logcat_process.send_signal(stop_signal)
+        try:
+            return_code = self.logcat_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS)
+        except subprocess.TimeoutExpired:
+            logging.error("[%s_%s] Failed to interrupt logcat process via SIGINT, sending SIGKILL" %
+                          (self.label, self.serial_number))
+            stop_signal = signal.SIGKILL
+            self.logcat_process.kill()
+            try:
+                return_code = self.logcat_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS)
+            except subprocess.TimeoutExpired:
+                logging.error("Failed to kill logcat_process %s %s" % (self.label, self.serial_number))
+                return_code = -65536
+        if return_code not in [-stop_signal, 0]:
+            logging.error("logcat_process %s_%s stopped with code: %d" % (self.label, self.serial_number, return_code))
+        self.logcat_logger.stop()
+        self.cleanup_port_forwarding()
+        self.adb.pull("/data/misc/bluetooth/logs/btsnoop_hci.log %s" % os.path.join(self.log_path_base,
+                                                                                    "%s_btsnoop_hci.log" % self.label))
+
+    def cleanup_port_forwarding(self):
+        self.adb.remove_tcp_forward(self.grpc_port)
+        self.adb.remove_tcp_forward(self.grpc_root_server_port)
+        self.adb.reverse("--remove tcp:%d" % self.signal_port)
+
+    @staticmethod
+    def ensure_no_output(result):
+        """
+        Ensure a command has not output
+        """
+        asserts.assert_true(
+            result is None or len(result) == 0, msg="command returned something when it shouldn't: %s" % result)
+
+    def sync_device_time(self):
+        self.adb.shell("settings put global auto_time 0")
+        self.adb.shell("settings put global auto_time_zone 0")
+        device_tz = self.adb.shell("date +%z")
+        asserts.assert_true(device_tz, "date +%z must return device timezone, "
+                            "but returned {} instead".format(device_tz))
+        host_tz = time.strftime("%z")
+        if device_tz != host_tz:
+            target_timezone = utils.get_timezone_olson_id()
+            logging.debug("Device timezone %s does not match host timezone %s, "
+                          "syncing them by setting timezone to %s" % (device_tz, host_tz, target_timezone))
+            self.adb.shell("setprop persist.sys.timezone %s" % target_timezone)
+            self.reboot()
+            device_tz = self.adb.shell("date +%z")
+            asserts.assert_equal(
+                host_tz, device_tz, "Device timezone %s still does not match host "
+                "timezone %s after reset" % (device_tz, host_tz))
+        self.adb.shell("date %s" % time.strftime("%m%d%H%M%Y.%S"))
+        datetime_format = "%Y-%m-%dT%H:%M:%S%z"
+        try:
+            device_time = datetime.strptime(self.adb.shell("date +'%s'" % datetime_format), datetime_format)
+        except ValueError:
+            asserts.fail("Failed to get time after sync")
+            return
+        # Include ADB delay that might be longer in SSH environment
+        max_delta_seconds = 3
+        host_time = datetime.now(tz=device_time.tzinfo)
+        asserts.assert_almost_equal(
+            (device_time - host_time).total_seconds(),
+            0,
+            msg="Device time %s and host time %s off by >%dms after sync" %
+            (device_time.isoformat(), host_time.isoformat(), int(max_delta_seconds * 1000)),
+            delta=max_delta_seconds)
+
+    def push_or_die(self, src_file_path, dst_file_path, push_timeout=300):
+        """Pushes a file to the Android device
+
+        Args:
+            src_file_path: The path to the file to install.
+            dst_file_path: The destination of the file.
+            push_timeout: How long to wait for the push to finish in seconds
+        """
+        out = self.adb.push('%s %s' % (src_file_path, dst_file_path), timeout=push_timeout)
+        if 'error' in out:
+            asserts.fail('Unable to push file %s to %s due to %s' % (src_file_path, dst_file_path, out))
+
+    def tcp_forward_or_die(self, host_port, device_port, num_retry=1):
+        """
+        Forward a TCP port from host to device or fail
+        :param host_port: host port, int, 0 for adb to assign one
+        :param device_port: device port, int
+        :param num_retry: number of times to reboot and retry this before dying
+        :return: host port int
+        """
+        error_or_port = self.adb.tcp_forward(host_port, device_port)
+        if not error_or_port:
+            logging.debug("host port %d was already forwarded" % host_port)
+            return host_port
+        if not isinstance(error_or_port, int):
+            if num_retry > 0:
+                # If requested, reboot an retry
+                num_retry -= 1
+                logging.warning(
+                    "[%s] Failed to TCP forward host port %d to "
+                    "device port %d, num_retries left is %d" % (self.label, host_port, device_port, num_retry))
+                self.reboot()
+                return self.tcp_forward_or_die(host_port, device_port, num_retry=num_retry)
+            asserts.fail(
+                'Unable to forward host port %d to device port %d, error %s' % (host_port, device_port, error_or_port))
+        return error_or_port
+
+    def tcp_reverse_or_die(self, device_port, host_port, num_retry=1):
+        """
+        Forward a TCP port from device to host or fail
+        :param device_port: device port, int, 0 for adb to assign one
+        :param host_port: host port, int
+        :param num_retry: number of times to reboot and retry this before dying
+        :return: device port int
+        """
+        error_or_port = self.adb.reverse("tcp:%d tcp:%d" % (device_port, host_port))
+        if not error_or_port:
+            logging.debug("device port %d was already reversed" % device_port)
+            return device_port
+        try:
+            error_or_port = int(error_or_port)
+        except ValueError:
+            if num_retry > 0:
+                # If requested, reboot an retry
+                num_retry -= 1
+                logging.warning(
+                    "[%s] Failed to TCP reverse device port %d to "
+                    "host port %d, num_retries left is %d" % (self.label, device_port, host_port, num_retry))
+                self.reboot()
+                return self.tcp_reverse_or_die(device_port, host_port, num_retry=num_retry)
+            asserts.fail(
+                'Unable to reverse device port %d to host port %d, error %s' % (device_port, host_port, error_or_port))
+        return error_or_port
+
+    def ensure_verity_disabled(self):
+        """Ensures that verity is enabled.
+
+        If verity is not enabled, this call will reboot the phone. Note that
+        this only works on debuggable builds.
+        """
+        logging.debug("Disabling verity and remount for %s", self.serial_number)
+        # The below properties will only exist if verity has been enabled.
+        system_verity = self.adb.getprop('partition.system.verified')
+        vendor_verity = self.adb.getprop('partition.vendor.verified')
+        if system_verity or vendor_verity:
+            self.adb.disable_verity()
+            self.reboot()
+        self.adb.remount()
+        self.adb.wait_for_device(timeout=self.WAIT_FOR_DEVICE_TIMEOUT_SECONDS)
+
+    def reboot(self, timeout_minutes=15.0):
+        """Reboots the device.
+
+        Reboot the device, wait for device to complete booting.
+        """
+        logging.debug("Rebooting %s", self.serial_number)
+        self.adb.reboot()
+
+        timeout_start = time.time()
+        timeout = timeout_minutes * 60
+        # Android sometimes return early after `adb reboot` is called. This
+        # means subsequent calls may make it to the device before the reboot
+        # goes through, return false positives for getprops such as
+        # sys.boot_completed.
+        while time.time() < timeout_start + timeout:
+            try:
+                self.adb.get_state()
+                time.sleep(.1)
+            except AdbError:
+                # get_state will raise an error if the device is not found. We
+                # want the device to be missing to prove the device has kicked
+                # off the reboot.
+                break
+        minutes_left = timeout_minutes - (time.time() - timeout_start) / 60.0
+        self.wait_for_boot_completion(timeout_minutes=minutes_left)
+        asserts.assert_true(self.adb.ensure_root(), "device %s cannot run as root after reboot", self.serial_number)
+
+    def wait_for_boot_completion(self, timeout_minutes=15.0):
+        """
+        Waits for Android framework to broadcast ACTION_BOOT_COMPLETED.
+        :param timeout_minutes: number of minutes to wait
+        """
+        timeout_start = time.time()
+        timeout = timeout_minutes * 60
+
+        self.adb.wait_for_device(timeout=self.WAIT_FOR_DEVICE_TIMEOUT_SECONDS)
+        while time.time() < timeout_start + timeout:
+            try:
+                completed = self.adb.getprop("sys.boot_completed")
+                if completed == '1':
+                    return
+            except AdbError:
+                # adb shell calls may fail during certain period of booting
+                # process, which is normal. Ignoring these errors.
+                pass
+            time.sleep(5)
+        asserts.fail(msg='Device %s booting process timed out.' % self.serial_number)
diff --git a/gd/cert/gd_device_base.py b/gd/cert/gd_device_base.py
deleted file mode 100644
index df5f9f2..0000000
--- a/gd/cert/gd_device_base.py
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/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 logging
-import os
-from builtins import open
-import json
-import signal
-import socket
-import subprocess
-import time
-
-from acts import error
-from acts import tracelogger
-
-import grpc
-
-ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
-ANDROID_HOST_OUT = os.environ.get('ANDROID_HOST_OUT')
-WAIT_CHANNEL_READY_TIMEOUT = 10
-
-def replace_vars(string, config):
-    serial_number = config.get("serial_number")
-    if serial_number is None:
-        serial_number = ""
-    rootcanal_port = config.get("rootcanal_port")
-    if rootcanal_port is None:
-        rootcanal_port = ""
-    return string.replace("$ANDROID_HOST_OUT", ANDROID_HOST_OUT) \
-                 .replace("$(grpc_port)", config.get("grpc_port")) \
-                 .replace("$(grpc_root_server_port)", config.get("grpc_root_server_port")) \
-                 .replace("$(rootcanal_port)", rootcanal_port) \
-                 .replace("$(signal_port)", config.get("signal_port")) \
-                 .replace("$(serial_number)", serial_number)
-
-class GdDeviceBase:
-    def __init__(self, grpc_port, grpc_root_server_port, signal_port, cmd,
-                 label, type_identifier):
-        self.label = label if label is not None else grpc_port
-        # logging.log_path only exists when this is used in an ACTS test run.
-        log_path_base = getattr(logging, 'log_path', '/tmp/logs')
-        self.log = tracelogger.TraceLogger(
-            GdDeviceBaseLoggerAdapter(logging.getLogger(), {
-                'device': label,
-                'type_identifier' : type_identifier
-            }))
-
-        backing_process_logpath = os.path.join(
-            log_path_base, '%s_%s_backing_logs.txt' % (type_identifier, label))
-        self.backing_process_logs = open(backing_process_logpath, 'w')
-
-        cmd_str = json.dumps(cmd)
-        if "--btsnoop=" not in cmd_str:
-            btsnoop_path = os.path.join(log_path_base, '%s_btsnoop_hci.log' % label)
-            cmd.append("--btsnoop=" + btsnoop_path)
-
-        tester_signal_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        tester_signal_socket.setsockopt(
-            socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        socket_address = ('localhost', int(signal_port))
-        tester_signal_socket.bind(socket_address)
-        tester_signal_socket.listen(1)
-
-        self.backing_process = subprocess.Popen(
-            cmd,
-            cwd=ANDROID_BUILD_TOP,
-            env=os.environ.copy(),
-            stdout=self.backing_process_logs,
-            stderr=self.backing_process_logs)
-        tester_signal_socket.accept()
-        tester_signal_socket.close()
-
-        self.grpc_root_server_channel = grpc.insecure_channel("localhost:" + grpc_root_server_port)
-        self.grpc_port = int(grpc_port)
-        self.grpc_channel = grpc.insecure_channel("localhost:" + grpc_port)
-
-    def clean_up(self):
-        self.grpc_channel.close()
-        self.grpc_root_server_channel.close()
-        self.backing_process.send_signal(signal.SIGINT)
-        backing_process_return_code = self.backing_process.wait()
-        self.backing_process_logs.close()
-        if backing_process_return_code != 0:
-            logging.error("backing process %s stopped with code: %d" %
-                          (self.label, backing_process_return_code))
-            return False
-
-    def wait_channel_ready(self):
-        future = grpc.channel_ready_future(self.grpc_channel)
-        try:
-          future.result(timeout = WAIT_CHANNEL_READY_TIMEOUT)
-        except grpc.FutureTimeoutError:
-          logging.error("wait channel ready timeout")
-
-
-
-class GdDeviceBaseLoggerAdapter(logging.LoggerAdapter):
-    def process(self, msg, kwargs):
-        msg = "[%s|%s] %s" % (self.extra["type_identifier"], self.extra["device"], msg)
-        return (msg, kwargs)
-
-class GdDeviceConfigError(Exception):
-    """Raised when GdDevice configs are malformatted."""
-
-
-class GdDeviceError(error.ActsError):
-    """Raised when there is an error in GdDevice."""
-
diff --git a/gd/cert/gen_html_coverage.sh b/gd/cert/gen_html_coverage.sh
new file mode 100755
index 0000000..bfb1edb
--- /dev/null
+++ b/gd/cert/gen_html_coverage.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+pushd $ANDROID_BUILD_TOP
+
+llvm-cov show --format=html --summary-only --show-line-counts-or-regions --show-instantiation-summary --instr-profile=/tmp/logs/HostOnlyCert/latest/GdDevice_dut_backing_process_coverage.profdata --output-dir=/tmp/logs/HostOnlyCert/latest/GdDevice_dut_backing_process_coverage/ out/dist/bluetooth_venv/lib/python3.8/site-packages/bluetooth_stack_with_facade
+
+popd
+
+echo "point your browser to file:///tmp/logs/HostOnlyCert/latest/GdDevice_dut_backing_process_coverage/index.html"
+
diff --git a/gd/cert/grpc_root_server.cc b/gd/cert/grpc_root_server.cc
deleted file mode 100644
index 48b2d5a..0000000
--- a/gd/cert/grpc_root_server.cc
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "cert/grpc_root_server.h"
-
-#include <string>
-
-#include "cert/read_only_property_server.h"
-#include "cert/rootservice.grpc.pb.h"
-#include "grpc/grpc_module.h"
-#include "hal/cert/cert.h"
-#include "hci/cert/cert.h"
-#include "l2cap/classic/cert/cert.h"
-#include "os/log.h"
-#include "os/thread.h"
-#include "stack_manager.h"
-
-namespace bluetooth {
-namespace cert {
-
-using ::bluetooth::grpc::GrpcModule;
-using ::bluetooth::os::Thread;
-
-namespace {
-class RootCertService : public ::bluetooth::cert::RootCert::Service {
- public:
-  RootCertService(int grpc_port) : grpc_port_(grpc_port) {}
-
-  ::grpc::Status StartStack(::grpc::ServerContext* context, const ::bluetooth::cert::StartStackRequest* request,
-                            ::bluetooth::cert::StartStackResponse* response) override {
-    if (is_running_) {
-      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is running");
-    }
-
-    ModuleList modules;
-    modules.add<::bluetooth::grpc::GrpcModule>();
-
-    BluetoothModule module_to_test = request->module_to_test();
-    switch (module_to_test) {
-      case BluetoothModule::HAL:
-        modules.add<::bluetooth::hal::cert::HalCertModule>();
-        break;
-      case BluetoothModule::HCI:
-        modules.add<::bluetooth::cert::ReadOnlyPropertyServerModule>();
-        modules.add<::bluetooth::hci::cert::AclManagerCertModule>();
-        break;
-      case BluetoothModule::L2CAP:
-        modules.add<::bluetooth::cert::ReadOnlyPropertyServerModule>();
-        modules.add<::bluetooth::l2cap::classic::cert::L2capClassicModuleCertModule>();
-        break;
-      default:
-        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "invalid module under test");
-    }
-
-    stack_thread_ = new Thread("stack_thread", Thread::Priority::NORMAL);
-    stack_manager_.StartUp(&modules, stack_thread_);
-
-    GrpcModule* grpc_module = stack_manager_.GetInstance<GrpcModule>();
-    grpc_module->StartServer("0.0.0.0", grpc_port_);
-
-    grpc_loop_thread_ = new std::thread([grpc_module] { grpc_module->RunGrpcLoop(); });
-    is_running_ = true;
-
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status StopStack(::grpc::ServerContext* context, const ::bluetooth::cert::StopStackRequest* request,
-                           ::bluetooth::cert::StopStackResponse* response) override {
-    if (!is_running_) {
-      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is not running");
-    }
-
-    stack_manager_.GetInstance<GrpcModule>()->StopServer();
-    grpc_loop_thread_->join();
-    delete grpc_loop_thread_;
-
-    stack_manager_.ShutDown();
-    delete stack_thread_;
-    is_running_ = false;
-    return ::grpc::Status::OK;
-  }
-
- private:
-  Thread* stack_thread_ = nullptr;
-  bool is_running_ = false;
-  std::thread* grpc_loop_thread_ = nullptr;
-  StackManager stack_manager_;
-  int grpc_port_ = 8898;
-};
-
-RootCertService* root_cert_service;
-}  // namespace
-
-void GrpcRootServer::StartServer(const std::string& address, int grpc_root_server_port, int grpc_port) {
-  ASSERT(!started_);
-  started_ = true;
-
-  std::string listening_port = address + ":" + std::to_string(grpc_root_server_port);
-  ::grpc::ServerBuilder builder;
-  root_cert_service = new RootCertService(grpc_port);
-  builder.RegisterService(root_cert_service);
-  builder.AddListeningPort(listening_port, ::grpc::InsecureServerCredentials());
-  server_ = builder.BuildAndStart();
-
-  ASSERT(server_ != nullptr);
-}
-
-void GrpcRootServer::StopServer() {
-  ASSERT(started_);
-  server_->Shutdown();
-  started_ = false;
-  server_.reset();
-  delete root_cert_service;
-}
-
-void GrpcRootServer::RunGrpcLoop() {
-  ASSERT(started_);
-  server_->Wait();
-}
-
-}  // namespace cert
-}  // namespace bluetooth
diff --git a/gd/cert/host_config.json b/gd/cert/host_config.json
new file mode 100644
index 0000000..5b24dc7
--- /dev/null
+++ b/gd/cert/host_config.json
@@ -0,0 +1,50 @@
+
+{   "_description": "Bluetooth cert testing",
+    "testbed":
+    [
+        {
+            "_description": "Host only cert testbed",
+            "name": "HostOnlyCert",
+            "rootcanal":
+            {
+                "test_port": "6401",
+                "hci_port": "6402",
+                "link_layer_port": "6403"
+            },
+            "GdDevice":
+            [
+                {
+                    "grpc_port": "8998",
+                    "grpc_root_server_port": "8996",
+                    "signal_port": "8994",
+                    "label": "cert",
+                    "name": "Cert Device",
+                    "cmd":
+                    [
+                        "$GD_ROOT/bluetooth_stack_with_facade",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--rootcanal-port=$(rootcanal_port)",
+                        "--signal-port=$(signal_port)"
+                    ]
+                },
+                {
+                    "grpc_port": "8999",
+                    "grpc_root_server_port": "8997",
+                    "signal_port": "8995",
+                    "label": "dut",
+                    "name": "DUT Device",
+                    "cmd":
+                    [
+                        "$GD_ROOT/bluetooth_stack_with_facade",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--rootcanal-port=$(rootcanal_port)",
+                        "--signal-port=$(signal_port)"
+                    ]
+                }
+            ]
+        }
+    ],
+    "logpath": "/tmp/logs"
+}
diff --git a/gd/cert/host_only_config.json b/gd/cert/host_only_config.json
deleted file mode 100644
index 75e459c..0000000
--- a/gd/cert/host_only_config.json
+++ /dev/null
@@ -1,50 +0,0 @@
-{   "_description": "Bluetooth cert testing",
-    "testbed":
-    [
-        {
-            "_description": "Host only cert testbed",
-            "name": "HostOnlyCert",
-            "rootcanal":
-            {
-                "test_port": 6401,
-                "hci_port": 6402,
-                "link_layer_port": 6403
-            },
-            "GdDevice":
-            [
-                {
-                    "grpc_port": "8899",
-                    "grpc_root_server_port": "8897",
-                    "signal_port": "8895",
-                    "label": "stack_under_test",
-                    "cmd":
-                    [
-                        "$ANDROID_HOST_OUT/bin/bluetooth_stack_with_facade",
-                        "--grpc-port=$(grpc_port)",
-                        "--root-server-port=$(grpc_root_server_port)",
-                        "--rootcanal-port=$(rootcanal_port)",
-                        "--signal-port=$(signal_port)"
-                    ]
-                }
-            ],
-            "GdCertDevice":
-            [
-                {
-                    "grpc_port": "8898",
-                    "grpc_root_server_port": "8896",
-                    "signal_port": "8894",
-                    "label": "cert_stack",
-                    "cmd":
-                    [
-                        "$ANDROID_HOST_OUT/bin/bluetooth_cert_stack",
-                        "--grpc-port=$(grpc_port)",
-                        "--root-server-port=$(grpc_root_server_port)",
-                        "--rootcanal-port=$(rootcanal_port)",
-                        "--signal-port=$(signal_port)"
-                    ]
-                }
-            ]
-        }
-    ],
-    "logpath": "/tmp/logs"
-}
diff --git a/gd/cert/logging_client_interceptor.py b/gd/cert/logging_client_interceptor.py
new file mode 100644
index 0000000..8e9c25e
--- /dev/null
+++ b/gd/cert/logging_client_interceptor.py
@@ -0,0 +1,78 @@
+#!/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 logging
+
+import grpc
+
+from google.protobuf import text_format
+
+
+def pretty_print(request):
+    return '{} {}'.format(type(request).__name__, text_format.MessageToString(request, as_one_line=True))
+
+
+class LoggingRandezvousWrapper():
+
+    def __init__(self, server_stream_call, logTag):
+        if server_stream_call is None:
+            raise ValueError("server_stream_call cannot be None")
+        self.server_stream_call = server_stream_call
+        self.logTag = logTag
+
+    def cancel(self):
+        self.server_stream_call.cancel()
+
+    def cancelled(self):
+        return self.server_stream_call.cancelled()
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        resp = self.server_stream_call.__next__()
+        print("%s %s" % (self.logTag, pretty_print(resp)))
+        return resp
+
+
+class LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor):
+
+    TAG_MIN_WIDTH = 24
+
+    def __init__(self, name):
+        self.name = name
+        self.inLogTag = "[host ▶▶▶▶▶ %s]" % self.name
+        self.outLogTag = "[host ◀◀◀◀◀ %s]" % self.name
+        tagLength = len(re.sub('[^\w\s]', '', self.inLogTag)) + 11
+        if tagLength < self.TAG_MIN_WIDTH:
+            self.inLogTag += " " * (self.TAG_MIN_WIDTH - tagLength)
+            self.outLogTag += " " * (self.TAG_MIN_WIDTH - tagLength)
+
+    def intercept_unary_unary(self, continuation, client_call_details, request):
+        """
+        This interceptor logs the requests from host
+        """
+        print("%s%s %s" % (self.inLogTag, client_call_details.method, pretty_print(request)))
+        return continuation(client_call_details, request)
+
+    def intercept_unary_stream(self, continuation, client_call_details, request):
+        """
+        This interceptor wraps the server response, and logs all the messages coming to host
+        """
+        print("%s%s %s" % (self.inLogTag, client_call_details.method, pretty_print(request)))
+        server_stream_call = continuation(client_call_details, request)
+        retuningMsgLogTag = self.outLogTag + client_call_details.method
+        return LoggingRandezvousWrapper(server_stream_call, retuningMsgLogTag)
diff --git a/gd/cert/matchers.py b/gd/cert/matchers.py
new file mode 100644
index 0000000..cb78b25
--- /dev/null
+++ b/gd/cert/matchers.py
@@ -0,0 +1,596 @@
+#!/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 bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets
+from bluetooth_packets_python3.hci_packets import EventCode
+from bluetooth_packets_python3 import l2cap_packets
+from bluetooth_packets_python3.l2cap_packets import CommandCode, LeCommandCode
+from bluetooth_packets_python3.l2cap_packets import ConfigurationResponseResult
+from bluetooth_packets_python3.l2cap_packets import ConnectionResponseResult
+from bluetooth_packets_python3.l2cap_packets import InformationRequestInfoType
+from bluetooth_packets_python3.l2cap_packets import LeCreditBasedConnectionResponseResult
+
+
+class HciMatchers(object):
+
+    @staticmethod
+    def CommandComplete(opcode=None, num_complete=1):
+        return lambda msg: HciMatchers._is_matching_command_complete(msg.event, opcode, num_complete)
+
+    @staticmethod
+    def _is_matching_command_complete(packet_bytes, opcode=None, num_complete=1):
+        hci_event = HciMatchers.extract_hci_event_with_code(packet_bytes, EventCode.COMMAND_COMPLETE)
+        if hci_event is None:
+            return False
+        frame = hci_packets.CommandCompleteView(hci_event)
+        return (opcode is None or frame.GetCommandOpCode() == opcode) and\
+               frame.GetNumHciCommandPackets() == num_complete
+
+    @staticmethod
+    def EventWithCode(event_code):
+        return lambda msg: HciMatchers.extract_hci_event_with_code(msg.event, event_code)
+
+    @staticmethod
+    def extract_hci_event_with_code(packet_bytes, event_code=None):
+        hci_event = hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if hci_event is None:
+            return None
+        if event_code is not None and hci_event.GetEventCode() != event_code:
+            return None
+        return hci_event
+
+    @staticmethod
+    def LinkKeyRequest():
+        return lambda event: HciMatchers.EventWithCode(EventCode.LINK_KEY_REQUEST)
+
+    @staticmethod
+    def IoCapabilityRequest():
+        return lambda event: HciMatchers.EventWithCode(EventCode.IO_CAPABILITY_REQUEST)
+
+    @staticmethod
+    def IoCapabilityResponse():
+        return lambda event: HciMatchers.EventWithCode(EventCode.IO_CAPABILITY_RESPONSE)
+
+    @staticmethod
+    def UserPasskeyNotification():
+        return lambda event: HciMatchers.EventWithCode(EventCode.USER_PASSKEY_NOTIFICATION)
+
+    @staticmethod
+    def UserPasskeyRequest():
+        return lambda event: HciMatchers.EventWithCode(EventCode.USER_PASSKEY_REQUEST)
+
+    @staticmethod
+    def UserConfirmationRequest():
+        return lambda event: HciMatchers.EventWithCode(EventCode.USER_CONFIRMATION_REQUEST)
+
+    @staticmethod
+    def RemoteHostSupportedFeaturesNotification():
+        return lambda event: HciMatchers.EventWithCode(EventCode.REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION)
+
+    @staticmethod
+    def LinkKeyNotification():
+        return lambda event: HciMatchers.EventWithCode(EventCode.LINK_KEY_NOTIFICATION)
+
+    @staticmethod
+    def SimplePairingComplete():
+        return lambda event: HciMatchers.EventWithCode(EventCode.SIMPLE_PAIRING_COMPLETE)
+
+    @staticmethod
+    def Disconnect():
+        return lambda event: HciMatchers.EventWithCode(EventCode.DISCONNECT)
+
+
+class NeighborMatchers(object):
+
+    @staticmethod
+    def InquiryResult(address):
+        return lambda msg: NeighborMatchers._is_matching_inquiry_result(msg.packet, address)
+
+    @staticmethod
+    def _is_matching_inquiry_result(packet, address):
+        hci_event = HciMatchers.extract_hci_event_with_code(packet, EventCode.INQUIRY_RESULT)
+        if hci_event is None:
+            return False
+        inquiry_view = hci_packets.InquiryResultView(hci_event)
+        if inquiry_view is None:
+            return False
+        results = inquiry_view.GetInquiryResults()
+        return any((address == result.bd_addr for result in results))
+
+    @staticmethod
+    def InquiryResultwithRssi(address):
+        return lambda msg: NeighborMatchers._is_matching_inquiry_result_with_rssi(msg.packet, address)
+
+    @staticmethod
+    def _is_matching_inquiry_result_with_rssi(packet, address):
+        hci_event = HciMatchers.extract_hci_event_with_code(packet, EventCode.INQUIRY_RESULT_WITH_RSSI)
+        if hci_event is None:
+            return False
+        inquiry_view = hci_packets.InquiryResultWithRssiView(hci_event)
+        if inquiry_view is None:
+            return False
+        results = inquiry_view.GetInquiryResults()
+        return any((address == result.address for result in results))
+
+    @staticmethod
+    def ExtendedInquiryResult(address):
+        return lambda msg: NeighborMatchers._is_matching_extended_inquiry_result(msg.packet, address)
+
+    @staticmethod
+    def _is_matching_extended_inquiry_result(packet, address):
+        hci_event = HciMatchers.extract_hci_event_with_code(packet, EventCode.EXTENDED_INQUIRY_RESULT)
+        if hci_event is None:
+            return False
+        extended_view = hci_packets.ExtendedInquiryResultView(hci_event)
+        if extended_view is None:
+            return False
+        return address == extended_view.GetAddress()
+
+
+class L2capMatchers(object):
+
+    @staticmethod
+    def ConnectionRequest(psm):
+        return lambda packet: L2capMatchers._is_matching_connection_request(packet, psm)
+
+    @staticmethod
+    def ConnectionResponse(scid):
+        return lambda packet: L2capMatchers._is_matching_connection_response(packet, scid)
+
+    @staticmethod
+    def ConfigurationResponse(result=ConfigurationResponseResult.SUCCESS):
+        return lambda packet: L2capMatchers._is_matching_configuration_response(packet, result)
+
+    @staticmethod
+    def ConfigurationRequest(cid=None):
+        return lambda packet: L2capMatchers._is_matching_configuration_request_with_cid(packet, cid)
+
+    @staticmethod
+    def ConfigurationRequestWithErtm():
+        return lambda packet: L2capMatchers._is_matching_configuration_request_with_ertm(packet)
+
+    @staticmethod
+    def ConfigurationRequestView(dcid):
+        return lambda request_view: request_view.GetDestinationCid() == dcid
+
+    @staticmethod
+    def DisconnectionRequest(scid, dcid):
+        return lambda packet: L2capMatchers._is_matching_disconnection_request(packet, scid, dcid)
+
+    @staticmethod
+    def DisconnectionResponse(scid, dcid):
+        return lambda packet: L2capMatchers._is_matching_disconnection_response(packet, scid, dcid)
+
+    @staticmethod
+    def EchoResponse():
+        return lambda packet: L2capMatchers._is_control_frame_with_code(packet, CommandCode.ECHO_RESPONSE)
+
+    @staticmethod
+    def CommandReject():
+        return lambda packet: L2capMatchers._is_control_frame_with_code(packet, CommandCode.COMMAND_REJECT)
+
+    @staticmethod
+    def LeCommandReject():
+        return lambda packet: L2capMatchers._is_le_control_frame_with_code(packet, LeCommandCode.COMMAND_REJECT)
+
+    @staticmethod
+    def LeConnectionParameterUpdateRequest():
+        return lambda packet: L2capMatchers._is_le_control_frame_with_code(
+            packet, LeCommandCode.CONNECTION_PARAMETER_UPDATE_REQUEST)
+
+    @staticmethod
+    def LeConnectionParameterUpdateResponse(result=l2cap_packets.ConnectionParameterUpdateResponseResult.ACCEPTED):
+        return lambda packet: L2capMatchers._is_matching_connection_parameter_update_response(packet, result)
+
+    @staticmethod
+    def CreditBasedConnectionRequest(psm):
+        return lambda packet: L2capMatchers._is_matching_credit_based_connection_request(packet, psm)
+
+    @staticmethod
+    def CreditBasedConnectionResponse(result=LeCreditBasedConnectionResponseResult.SUCCESS):
+        return lambda packet: L2capMatchers._is_matching_credit_based_connection_response(packet, result)
+
+    @staticmethod
+    def CreditBasedConnectionResponseUsedCid():
+        return lambda packet: L2capMatchers._is_matching_credit_based_connection_response(
+            packet, LeCreditBasedConnectionResponseResult.SOURCE_CID_ALREADY_ALLOCATED
+        ) or L2capMatchers._is_le_control_frame_with_code(packet, LeCommandCode.COMMAND_REJECT)
+
+    @staticmethod
+    def LeDisconnectionRequest(scid, dcid):
+        return lambda packet: L2capMatchers._is_matching_le_disconnection_request(packet, scid, dcid)
+
+    @staticmethod
+    def LeDisconnectionResponse(scid, dcid):
+        return lambda packet: L2capMatchers._is_matching_le_disconnection_response(packet, scid, dcid)
+
+    @staticmethod
+    def LeFlowControlCredit(cid):
+        return lambda packet: L2capMatchers._is_matching_le_flow_control_credit(packet, cid)
+
+    @staticmethod
+    def SFrame(req_seq=None, f=None, s=None, p=None):
+        return lambda packet: L2capMatchers._is_matching_supervisory_frame(packet, req_seq, f, s, p)
+
+    @staticmethod
+    def IFrame(tx_seq=None, payload=None, f=None):
+        return lambda packet: L2capMatchers._is_matching_information_frame(packet, tx_seq, payload, f, fcs=False)
+
+    @staticmethod
+    def IFrameWithFcs(tx_seq=None, payload=None, f=None):
+        return lambda packet: L2capMatchers._is_matching_information_frame(packet, tx_seq, payload, f, fcs=True)
+
+    @staticmethod
+    def IFrameStart(tx_seq=None, payload=None, f=None):
+        return lambda packet: L2capMatchers._is_matching_information_start_frame(packet, tx_seq, payload, f, fcs=False)
+
+    @staticmethod
+    def Data(payload):
+        return lambda packet: packet.GetPayload().GetBytes() == payload
+
+    @staticmethod
+    def FirstLeIFrame(payload, sdu_size):
+        return lambda packet: L2capMatchers._is_matching_first_le_i_frame(packet, payload, sdu_size)
+
+    # this is a hack - should be removed
+    @staticmethod
+    def PartialData(payload):
+        return lambda packet: payload in packet.GetPayload().GetBytes()
+
+    # this is a hack - should be removed
+    @staticmethod
+    def PacketPayloadRawData(payload):
+        return lambda packet: payload in packet.payload
+
+    # this is a hack - should be removed
+    @staticmethod
+    def PacketPayloadWithMatchingPsm(psm):
+        return lambda packet: None if psm != packet.psm else packet
+
+    # this is a hack - should be removed
+    @staticmethod
+    def PacketPayloadWithMatchingCid(cid):
+        return lambda packet: None if cid != packet.fixed_cid else packet
+
+    @staticmethod
+    def ExtractBasicFrame(scid):
+        return lambda packet: L2capMatchers._basic_frame_for(packet, scid)
+
+    @staticmethod
+    def ExtractBasicFrameWithFcs(scid):
+        return lambda packet: L2capMatchers._basic_frame_with_fcs_for(packet, scid)
+
+    @staticmethod
+    def InformationRequestWithType(info_type):
+        return lambda packet: L2capMatchers._information_request_with_type(packet, info_type)
+
+    @staticmethod
+    def InformationResponseExtendedFeatures(supports_ertm=None,
+                                            supports_streaming=None,
+                                            supports_fcs=None,
+                                            supports_fixed_channels=None):
+        return lambda packet: L2capMatchers._is_matching_information_response_extended_features(
+            packet, supports_ertm, supports_streaming, supports_fcs, supports_fixed_channels)
+
+    @staticmethod
+    def _basic_frame(packet):
+        if packet is None:
+            return None
+        return l2cap_packets.BasicFrameView(bt_packets.PacketViewLittleEndian(list(packet.payload)))
+
+    @staticmethod
+    def _basic_frame_with_fcs(packet):
+        if packet is None:
+            return None
+        return l2cap_packets.BasicFrameWithFcsView(bt_packets.PacketViewLittleEndian(list(packet.payload)))
+
+    @staticmethod
+    def _basic_frame_for(packet, scid):
+        frame = L2capMatchers._basic_frame(packet)
+        if frame.GetChannelId() != scid:
+            return None
+        return frame
+
+    @staticmethod
+    def _basic_frame_with_fcs_for(packet, scid):
+        frame = L2capMatchers._basic_frame(packet)
+        if frame.GetChannelId() != scid:
+            return None
+        frame = L2capMatchers._basic_frame_with_fcs(packet)
+        if frame is None:
+            return None
+        return frame
+
+    @staticmethod
+    def _information_frame(packet):
+        standard_frame = l2cap_packets.StandardFrameView(packet)
+        if standard_frame.GetFrameType() != l2cap_packets.FrameType.I_FRAME:
+            return None
+        return l2cap_packets.EnhancedInformationFrameView(standard_frame)
+
+    @staticmethod
+    def _information_frame_with_fcs(packet):
+        standard_frame = l2cap_packets.StandardFrameWithFcsView(packet)
+        if standard_frame is None:
+            return None
+        if standard_frame.GetFrameType() != l2cap_packets.FrameType.I_FRAME:
+            return None
+        return l2cap_packets.EnhancedInformationFrameWithFcsView(standard_frame)
+
+    @staticmethod
+    def _information_start_frame(packet):
+        start_frame = L2capMatchers._information_frame(packet)
+        if start_frame is None:
+            return None
+        return l2cap_packets.EnhancedInformationStartFrameView(start_frame)
+
+    @staticmethod
+    def _information_start_frame_with_fcs(packet):
+        start_frame = L2capMatchers._information_frame_with_fcs(packet)
+        if start_frame is None:
+            return None
+        return l2cap_packets.EnhancedInformationStartFrameWithFcsView(start_frame)
+
+    @staticmethod
+    def _supervisory_frame(packet):
+        standard_frame = l2cap_packets.StandardFrameView(packet)
+        if standard_frame.GetFrameType() != l2cap_packets.FrameType.S_FRAME:
+            return None
+        return l2cap_packets.EnhancedSupervisoryFrameView(standard_frame)
+
+    @staticmethod
+    def _is_matching_information_frame(packet, tx_seq, payload, f, fcs=False):
+        if fcs:
+            frame = L2capMatchers._information_frame_with_fcs(packet)
+        else:
+            frame = L2capMatchers._information_frame(packet)
+        if frame is None:
+            return False
+        if tx_seq is not None and frame.GetTxSeq() != tx_seq:
+            return False
+        if payload is not None and frame.GetPayload().GetBytes() != payload:
+            return False
+        if f is not None and frame.GetF() != f:
+            return False
+        return True
+
+    @staticmethod
+    def _is_matching_information_start_frame(packet, tx_seq, payload, f, fcs=False):
+        if fcs:
+            frame = L2capMatchers._information_start_frame_with_fcs(packet)
+        else:
+            frame = L2capMatchers._information_start_frame(packet)
+        if frame is None:
+            return False
+        if tx_seq is not None and frame.GetTxSeq() != tx_seq:
+            return False
+        if payload is not None and frame.GetPayload().GetBytes() != payload:
+            return False
+        if f is not None and frame.GetF() != f:
+            return False
+        return True
+
+    @staticmethod
+    def _is_matching_supervisory_frame(packet, req_seq, f, s, p):
+        frame = L2capMatchers._supervisory_frame(packet)
+        if frame is None:
+            return False
+        if req_seq is not None and frame.GetReqSeq() != req_seq:
+            return False
+        if f is not None and frame.GetF() != f:
+            return False
+        if s is not None and frame.GetS() != s:
+            return False
+        if p is not None and frame.GetP() != p:
+            return False
+        return True
+
+    @staticmethod
+    def _is_matching_first_le_i_frame(packet, payload, sdu_size):
+        first_le_i_frame = l2cap_packets.FirstLeInformationFrameView(packet)
+        return first_le_i_frame.GetPayload().GetBytes() == payload and first_le_i_frame.GetL2capSduLength() == sdu_size
+
+    @staticmethod
+    def _control_frame(packet):
+        if packet.GetChannelId() != 1:
+            return None
+        return l2cap_packets.ControlView(packet.GetPayload())
+
+    @staticmethod
+    def _le_control_frame(packet):
+        if packet.GetChannelId() != 5:
+            return None
+        return l2cap_packets.LeControlView(packet.GetPayload())
+
+    @staticmethod
+    def control_frame_with_code(packet, code):
+        frame = L2capMatchers._control_frame(packet)
+        if frame is None or frame.GetCode() != code:
+            return None
+        return frame
+
+    @staticmethod
+    def le_control_frame_with_code(packet, code):
+        frame = L2capMatchers._le_control_frame(packet)
+        if frame is None or frame.GetCode() != code:
+            return None
+        return frame
+
+    @staticmethod
+    def _is_control_frame_with_code(packet, code):
+        return L2capMatchers.control_frame_with_code(packet, code) is not None
+
+    @staticmethod
+    def _is_le_control_frame_with_code(packet, code):
+        return L2capMatchers.le_control_frame_with_code(packet, code) is not None
+
+    @staticmethod
+    def _is_matching_connection_request(packet, psm):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONNECTION_REQUEST)
+        if frame is None:
+            return False
+        request = l2cap_packets.ConnectionRequestView(frame)
+        return request.GetPsm() == psm
+
+    @staticmethod
+    def _is_matching_connection_response(packet, scid):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONNECTION_RESPONSE)
+        if frame is None:
+            return False
+        response = l2cap_packets.ConnectionResponseView(frame)
+        return response.GetSourceCid() == scid and response.GetResult(
+        ) == ConnectionResponseResult.SUCCESS and response.GetDestinationCid() != 0
+
+    @staticmethod
+    def _is_matching_configuration_request_with_cid(packet, cid=None):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_REQUEST)
+        if frame is None:
+            return False
+        request = l2cap_packets.ConfigurationRequestView(frame)
+        dcid = request.GetDestinationCid()
+        return cid is None or cid == dcid
+
+    @staticmethod
+    def _is_matching_configuration_request_with_ertm(packet):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_REQUEST)
+        if frame is None:
+            return False
+        request = l2cap_packets.ConfigurationRequestView(frame)
+        config_bytes = request.GetBytes()
+        # TODO(b/153189503): Use packet struct parser.
+        return b"\x04\x09\x03" in config_bytes
+
+    @staticmethod
+    def _is_matching_configuration_response(packet, result=ConfigurationResponseResult.SUCCESS):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_RESPONSE)
+        if frame is None:
+            return False
+        response = l2cap_packets.ConfigurationResponseView(frame)
+        return response.GetResult() == result
+
+    @staticmethod
+    def _is_matching_disconnection_request(packet, scid, dcid):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.DISCONNECTION_REQUEST)
+        if frame is None:
+            return False
+        request = l2cap_packets.DisconnectionRequestView(frame)
+        return request.GetSourceCid() == scid and request.GetDestinationCid() == dcid
+
+    @staticmethod
+    def _is_matching_disconnection_response(packet, scid, dcid):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.DISCONNECTION_RESPONSE)
+        if frame is None:
+            return False
+        response = l2cap_packets.DisconnectionResponseView(frame)
+        return response.GetSourceCid() == scid and response.GetDestinationCid() == dcid
+
+    @staticmethod
+    def _is_matching_le_disconnection_response(packet, scid, dcid):
+        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.DISCONNECTION_RESPONSE)
+        if frame is None:
+            return False
+        response = l2cap_packets.LeDisconnectionResponseView(frame)
+        return response.GetSourceCid() == scid and response.GetDestinationCid() == dcid
+
+    @staticmethod
+    def _is_matching_le_disconnection_request(packet, scid, dcid):
+        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.DISCONNECTION_REQUEST)
+        if frame is None:
+            return False
+        request = l2cap_packets.LeDisconnectionRequestView(frame)
+        return request.GetSourceCid() == scid and request.GetDestinationCid() == dcid
+
+    @staticmethod
+    def _is_matching_le_flow_control_credit(packet, cid):
+        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_FLOW_CONTROL_CREDIT)
+        if frame is None:
+            return False
+        request = l2cap_packets.LeFlowControlCreditView(frame)
+        return request.GetCid() == cid
+
+    @staticmethod
+    def _information_request_with_type(packet, info_type):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.INFORMATION_REQUEST)
+        if frame is None:
+            return None
+        request = l2cap_packets.InformationRequestView(frame)
+        if request.GetInfoType() != info_type:
+            return None
+        return request
+
+    @staticmethod
+    def _information_response_with_type(packet, info_type):
+        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.INFORMATION_RESPONSE)
+        if frame is None:
+            return None
+        response = l2cap_packets.InformationResponseView(frame)
+        if response.GetInfoType() != info_type:
+            return None
+        return response
+
+    @staticmethod
+    def _is_matching_information_response_extended_features(packet, supports_ertm, supports_streaming, supports_fcs,
+                                                            supports_fixed_channels):
+        frame = L2capMatchers._information_response_with_type(packet,
+                                                              InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED)
+        if frame is None:
+            return False
+        features = l2cap_packets.InformationResponseExtendedFeaturesView(frame)
+        if supports_ertm is not None and features.GetEnhancedRetransmissionMode() != supports_ertm:
+            return False
+        if supports_streaming is not None and features.GetStreamingMode != supports_streaming:
+            return False
+        if supports_fcs is not None and features.GetFcsOption() != supports_fcs:
+            return False
+        if supports_fixed_channels is not None and features.GetFixedChannels() != supports_fixed_channels:
+            return False
+        return True
+
+    @staticmethod
+    def _is_matching_connection_parameter_update_response(packet, result):
+        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.CONNECTION_PARAMETER_UPDATE_RESPONSE)
+        if frame is None:
+            return False
+        return l2cap_packets.ConnectionParameterUpdateResponseView(frame).GetResult() == result
+
+    @staticmethod
+    def _is_matching_credit_based_connection_request(packet, psm):
+        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_REQUEST)
+        if frame is None:
+            return False
+        request = l2cap_packets.LeCreditBasedConnectionRequestView(frame)
+        return request.GetLePsm() == psm
+
+    @staticmethod
+    def _is_matching_credit_based_connection_response(packet, result):
+        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_RESPONSE)
+        if frame is None:
+            return False
+        response = l2cap_packets.LeCreditBasedConnectionResponseView(frame)
+        return response.GetResult() == result and (result != LeCreditBasedConnectionResponseResult.SUCCESS or
+                                                   response.GetDestinationCid() != 0)
+
+
+class SecurityMatchers(object):
+
+    @staticmethod
+    def UiMsg(type):
+        return lambda event: True if event.message_type == type else False
+
+    @staticmethod
+    def BondMsg(type):
+        return lambda event: True if event.message_type == type else False
diff --git a/gd/cert/metadata.py b/gd/cert/metadata.py
new file mode 100644
index 0000000..17579ae
--- /dev/null
+++ b/gd/cert/metadata.py
@@ -0,0 +1,78 @@
+#!/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 functools
+import inspect
+
+from mobly import asserts
+
+from acts.test_decorators import test_info
+
+
+def _fail_decorator(msg):
+
+    def fail_decorator(func):
+
+        @functools.wraps(func)
+        def fail(*args, **kwargs):
+            asserts.fail(msg)
+
+        return fail
+
+    return fail_decorator
+
+
+def metadata(_do_not_use=None, pts_test_id=None, pts_test_name=None):
+    """
+    Record a piece of test metadata in the Extra section of the test Record in
+    the test summary file. The metadata will come with a timestamp, but there
+    is no guarantee on the order of when the metadata will be written
+
+    Note:
+    - Metadata is recorded per test case as key-value pairs.
+    - Metadata is only guaranteed to be written when the test result is PASS,
+      FAIL or SKIPPED. When there are test infrastructural errors, metadata
+      might not be written successfully
+    :param _do_not_use: a positional argument with default value. This argument
+                        is to ensure that @metadata(key=value) is used in a
+                        functional form instead of @metadata or @metadata(a)
+    :param pts_test_id: A fully qualified PTS test ID such as
+                        L2CAP/COS/IEX/BV-01-C
+    :param pts_test_name: A human readable test name such as
+                          "Request Connection" for the above example
+    :return: decorated test case function object
+    """
+    if _do_not_use is not None:
+
+        def fail(*args, **kwargs):
+            asserts.fail("@metadata must be used in functional form such " "as @metadta(key=value)")
+
+        return fail
+
+    # Create a dictionary of optional parameters
+    values = locals()
+    args = {arg: values[arg] for arg in inspect.getfullargspec(metadata).args}
+    del args["_do_not_use"]
+
+    # Check if at least one optional parameter is valid
+    if not any(args.values()):
+        return _fail_decorator("at least one optional argument should be valid")
+
+    # Validate pts_test_id and pts_test_name
+    if any((pts_test_id, pts_test_name)) and \
+            not all((pts_test_id, pts_test_name)):
+        return _fail_decorator("pts_test_id and pts_test_name must both " "be valid if one of them is valid")
+
+    return test_info(**args)
diff --git a/gd/cert/os_utils.py b/gd/cert/os_utils.py
new file mode 100644
index 0000000..6a2f74e
--- /dev/null
+++ b/gd/cert/os_utils.py
@@ -0,0 +1,141 @@
+#!/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 logging
+from pathlib import Path
+import psutil
+import re
+import subprocess
+from typing import Container
+from collections import deque
+
+
+class TerminalColor:
+    RED = "\033[31;1m"
+    BLUE = "\033[34;1m"
+    YELLOW = "\033[33;1m"
+    MAGENTA = "\033[35;1m"
+    END = "\033[0m"
+
+
+def is_subprocess_alive(process, timeout_seconds=1):
+    """
+    Check if a process is alive for at least timeout_seconds
+    :param process: a Popen object that represent a subprocess
+    :param timeout_seconds: process needs to be alive for at least
+           timeout_seconds
+    :return: True if process is alive for at least timeout_seconds
+    """
+    try:
+        process.wait(timeout=timeout_seconds)
+        return False
+    except subprocess.TimeoutExpired as exp:
+        return True
+
+
+def get_gd_root():
+    """
+    Return the root of the GD test library
+
+    GD root is the parent directory of cert
+    :return: root directory string of gd test library
+    """
+    return str(Path(__file__).absolute().parents[1])
+
+
+def make_ports_available(ports: Container[int], timeout_seconds=10):
+    """Make sure a list of ports are available
+    kill occupying process if possible
+    :param ports: list of target ports
+    :param timeout_seconds: number of seconds to wait when killing processes
+    :return: True on success, False on failure
+    """
+    if not ports:
+        logging.warning("Empty ports is given to make_ports_available()")
+        return True
+    # Get connections whose state are in LISTEN only
+    # Connections in other states won't affect binding as SO_REUSEADDR is used
+    listening_conns_for_port = filter(
+        lambda conn: (conn and conn.status == psutil.CONN_LISTEN and conn.laddr and conn.laddr.port in ports),
+        psutil.net_connections())
+    success = True
+    for conn in listening_conns_for_port:
+        logging.warning("Freeing port %d used by %s" % (conn.laddr.port, str(conn)))
+        if not conn.pid:
+            logging.error("Failed to kill process occupying port %d due to lack of pid" % conn.laddr.port)
+            success = False
+            continue
+        logging.warning("Killing pid %d that is using port port %d" % (conn.pid, conn.laddr.port))
+        process = psutil.Process(conn.pid)
+        process.kill()
+        try:
+            process.wait(timeout=timeout_seconds)
+        except psutil.TimeoutExpired:
+            logging.error("SIGKILL timeout after %d seconds for pid %d" % (timeout_seconds, conn.pid))
+            continue
+    return success
+
+
+# e.g. 2020-05-06 16:02:04.216 bt - system/bt/gd/facade/facade_main.cc:79 - crash_callback: #03 pc 0000000000013520  /lib/x86_64-linux-gnu/libpthread-2.29.so
+HOST_CRASH_LINE_REGEX = re.compile(r"^.* - crash_callback: (?P<line>.*)$")
+HOST_ABORT_HEADER = "Process crashed, signal: Aborted"
+ASAN_OUTPUT_START_REGEX = re.compile(r"^==.*AddressSanitizer.*$")
+
+
+def read_crash_snippet_and_log_tail(logpath):
+    """
+    Get crash snippet if regex matched or last 20 lines of log
+    :return: crash_snippet, log_tail_20
+            1) crash snippet without timestamp in one string;
+            2) last 20 lines of log in one string;
+    """
+    gd_root_prefix = get_gd_root() + "/"
+    abort_line = None
+    last_20_lines = deque(maxlen=20)
+    crash_log_lines = []
+    asan = False
+    asan_lines = []
+
+    with open(logpath) as f:
+        for _, line in enumerate(f):
+            last_20_lines.append(line)
+            asan_match = ASAN_OUTPUT_START_REGEX.match(line)
+            if asan or asan_match:
+                asan_lines.append(line)
+                asan = True
+                continue
+
+            host_crash_match = HOST_CRASH_LINE_REGEX.match(line)
+            if host_crash_match:
+                crash_line = host_crash_match.group("line").replace(gd_root_prefix, "")
+                if HOST_ABORT_HEADER in crash_line \
+                        and len(last_20_lines) > 1:
+                    abort_line = last_20_lines[-2]
+                crash_log_lines.append(crash_line)
+
+    log_tail_20 = "".join(last_20_lines)
+    crash_snippet = ""
+    if abort_line is not None:
+        crash_snippet += "abort log line:\n\n%s\n" % abort_line
+    crash_snippet += "\n".join(crash_log_lines)
+
+    if len(asan_lines) > 0:
+        return "".join(asan_lines), log_tail_20
+
+    if len(crash_log_lines) > 0:
+        return crash_snippet, log_tail_20
+
+    return None, log_tail_20
diff --git a/gd/cert/performance_test_logger.py b/gd/cert/performance_test_logger.py
new file mode 100644
index 0000000..006bbd6
--- /dev/null
+++ b/gd/cert/performance_test_logger.py
@@ -0,0 +1,71 @@
+#!/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 datetime import datetime
+
+
+class PerformanceTestLogger(object):
+    """
+    A helper class to log time points and intervals
+    """
+
+    def __init__(self):
+        self.base_timepoint = datetime.now()
+        # We use a dictionary of a list of timepoints
+        self.start_interval_points = {}
+        self.end_interval_points = {}
+        self.single_points = {}
+
+    def log_single_point(self, label=""):
+        if label not in self.single_points:
+            self.single_points[label] = []
+        self.single_points[label].append(datetime.now())
+
+    def start_interval(self, label=""):
+        if label not in self.start_interval_points:
+            self.start_interval_points[label] = []
+        self.start_interval_points[label].append(datetime.now())
+
+    def end_interval(self, label=""):
+        if label not in self.end_interval_points:
+            self.end_interval_points[label] = []
+        self.end_interval_points[label].append(datetime.now())
+
+    def _check_interval_label(self, label):
+        if label not in self.start_interval_points or label not in self.end_interval_points:
+            raise KeyError("label %s doesn't exist" % label)
+        if len(self.start_interval_points[label]) != len(self.end_interval_points[label]):
+            raise KeyError("label %s doesn't have correct start and end log" % label)
+
+    def get_duration_of_intervals(self, label):
+        """
+        Return the list of duration of the intervals with specified label.
+        """
+        self._check_interval_label(label)
+        intervals = []
+        for i in range(len(self.start_interval_points[label])):
+            interval = self.end_interval_points[label][i] - self.start_interval_points[label][i]
+            intervals.append(interval)
+        return intervals
+
+    def dump_intervals(self):
+        """
+        Gives an iterator of (iterator of label, start, end) over all labels
+        """
+        for label in self.start_interval_points:
+            self._check_interval_label(label)
+            yield ((label, self.start_interval_points[label][i], self.end_interval_points[label][i])
+                   for i in range(len(self.start_interval_points[label])))
diff --git a/gd/l2cap/pts/pts.json b/gd/cert/pts.json
similarity index 95%
rename from gd/l2cap/pts/pts.json
rename to gd/cert/pts.json
index 76a27be..b266c5a 100644
--- a/gd/l2cap/pts/pts.json
+++ b/gd/cert/pts.json
@@ -13,6 +13,7 @@
                     "signal_port": "8895",
                     "label": "stack_under_test",
                     "serial_number": "DUT",
+                    "name": "Cert Device",
                     "cmd":
                     [
                         "adb",
diff --git a/gd/cert/pts_base_test.py b/gd/cert/pts_base_test.py
index 07154ea..8099407 100644
--- a/gd/cert/pts_base_test.py
+++ b/gd/cert/pts_base_test.py
@@ -17,23 +17,13 @@
 from acts.base_test import BaseTestClass
 
 import importlib
-import logging
-import os
-import signal
-import sys
-import subprocess
-
-ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
-
-sys.path.append(ANDROID_BUILD_TOP + '/out/soong/.intermediates/system/bt/gd/BluetoothFacadeAndCertGeneratedStub_py/gen')
 
 
 class PTSBaseTestClass(BaseTestClass):
+
     def __init__(self, configs):
         BaseTestClass.__init__(self, configs)
 
         gd_devices = self.controller_configs.get("GdDevice")
 
-        self.register_controller(
-            importlib.import_module('cert.gd_device'),
-            builtin=True)
+        self.register_controller(importlib.import_module('cert.gd_device'), builtin=True)
diff --git a/gd/l2cap/pts/pts_l2cap_testcase b/gd/cert/pts_l2cap_testcase
similarity index 100%
rename from gd/l2cap/pts/pts_l2cap_testcase
rename to gd/cert/pts_l2cap_testcase
diff --git a/gd/cert/py_acl_manager.py b/gd/cert/py_acl_manager.py
new file mode 100644
index 0000000..bba58ad
--- /dev/null
+++ b/gd/cert/py_acl_manager.py
@@ -0,0 +1,97 @@
+#!/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 google.protobuf import empty_pb2 as empty_proto
+from cert.event_stream import EventStream
+from cert.event_stream import IEventStream
+from cert.captures import HciCaptures
+from cert.closable import Closable
+from cert.closable import safeClose
+from bluetooth_packets_python3 import hci_packets
+from cert.truth import assertThat
+from hci.facade import acl_manager_facade_pb2 as acl_manager_facade
+
+
+class PyAclManagerAclConnection(IEventStream, Closable):
+
+    def __init__(self, acl_manager, remote_addr, handle, event_stream):
+        self.acl_manager = acl_manager
+        self.handle = handle
+        self.remote_addr = remote_addr
+        self.connection_event_stream = event_stream
+        self.acl_stream = EventStream(self.acl_manager.FetchAclData(acl_manager_facade.HandleMsg(handle=self.handle)))
+
+    def disconnect(self, reason):
+        packet_bytes = bytes(hci_packets.DisconnectBuilder(self.handle, reason).Serialize())
+        self.acl_manager.ConnectionCommand(acl_manager_facade.ConnectionCommandMsg(packet=packet_bytes))
+
+    def close(self):
+        safeClose(self.connection_event_stream)
+        safeClose(self.acl_stream)
+
+    def wait_for_disconnection_complete(self):
+        disconnection_complete = HciCaptures.DisconnectionCompleteCapture()
+        assertThat(self.connection_event_stream).emits(disconnection_complete)
+        self.disconnect_reason = disconnection_complete.get().GetReason()
+
+    def send(self, data):
+        self.acl_manager.SendAclData(acl_manager_facade.AclData(handle=self.handle, payload=bytes(data)))
+
+    def get_event_queue(self):
+        return self.acl_stream.get_event_queue()
+
+
+class PyAclManager:
+
+    def __init__(self, device):
+        self.acl_manager = device.hci_acl_manager
+        self.incoming_connection_event_stream = None
+        self.outgoing_connection_event_stream = None
+
+    def close(self):
+        safeClose(self.incoming_connection_event_stream)
+        safeClose(self.outgoing_connection_event_stream)
+
+    def listen_for_an_incoming_connection(self):
+        assertThat(self.incoming_connection_event_stream).isNone()
+        self.incoming_connection_event_stream = EventStream(
+            self.acl_manager.FetchIncomingConnection(empty_proto.Empty()))
+
+    def initiate_connection(self, remote_addr):
+        assertThat(self.outgoing_connection_event_stream).isNone()
+        remote_addr_bytes = bytes(remote_addr, 'utf8') if type(remote_addr) is str else bytes(remote_addr)
+        self.outgoing_connection_event_stream = EventStream(
+            self.acl_manager.CreateConnection(acl_manager_facade.ConnectionMsg(address=remote_addr_bytes)))
+
+    def complete_connection(self, event_stream):
+        connection_complete = HciCaptures.ConnectionCompleteCapture()
+        assertThat(event_stream).emits(connection_complete)
+        complete = connection_complete.get()
+        handle = complete.GetConnectionHandle()
+        address = complete.GetBdAddr()
+        return PyAclManagerAclConnection(self.acl_manager, address, handle, event_stream)
+
+    def complete_incoming_connection(self):
+        assertThat(self.incoming_connection_event_stream).isNotNone()
+        event_stream = self.incoming_connection_event_stream
+        self.incoming_connection_event_stream = None
+        return self.complete_connection(event_stream)
+
+    def complete_outgoing_connection(self):
+        assertThat(self.outgoing_connection_event_stream).isNotNone()
+        event_stream = self.outgoing_connection_event_stream
+        self.outgoing_connection_event_stream = None
+        return self.complete_connection(event_stream)
diff --git a/gd/cert/py_hal.py b/gd/cert/py_hal.py
new file mode 100644
index 0000000..bce039e
--- /dev/null
+++ b/gd/cert/py_hal.py
@@ -0,0 +1,48 @@
+#!/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 google.protobuf import empty_pb2 as empty_proto
+from cert.event_stream import EventStream
+from cert.closable import Closable
+from cert.closable import safeClose
+from hal import facade_pb2 as hal_facade
+
+
+class PyHal(Closable):
+
+    def __init__(self, device):
+        self.device = device
+
+        self.hci_event_stream = EventStream(self.device.hal.FetchHciEvent(empty_proto.Empty()))
+        self.acl_stream = EventStream(self.device.hal.FetchHciAcl(empty_proto.Empty()))
+
+        # We don't deal with SCO for now
+
+    def close(self):
+        safeClose(self.hci_event_stream)
+        safeClose(self.acl_stream)
+
+    def get_hci_event_stream(self):
+        return self.hci_event_stream
+
+    def get_acl_stream(self):
+        return self.acl_stream
+
+    def send_hci_command(self, command):
+        self.device.hal.SendHciCommand(hal_facade.HciCommandPacket(payload=bytes(command)))
+
+    def send_acl(self, acl):
+        self.device.hal.SendHciAcl(hal_facade.HciAclPacket(payload=bytes(acl)))
diff --git a/gd/cert/py_hci.py b/gd/cert/py_hci.py
new file mode 100644
index 0000000..5c7e5e0
--- /dev/null
+++ b/gd/cert/py_hci.py
@@ -0,0 +1,156 @@
+#!/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 google.protobuf import empty_pb2 as empty_proto
+from cert.event_stream import EventStream
+from cert.event_stream import FilteringEventStream
+from cert.event_stream import IEventStream
+from cert.closable import Closable
+from cert.closable import safeClose
+from cert.captures import HciCaptures
+from bluetooth_packets_python3 import hci_packets
+from cert.truth import assertThat
+from hci.facade import facade_pb2 as hci_facade
+
+
+class PyHciAclConnection(IEventStream):
+
+    def __init__(self, handle, acl_stream, device):
+        self.handle = int(handle)
+        self.device = device
+        # todo, handle we got is 0, so doesn't match - fix before enabling filtering
+        self.our_acl_stream = FilteringEventStream(acl_stream, None)
+
+    def send(self, pb_flag, b_flag, data):
+        acl_msg = hci_facade.AclMsg(
+            handle=self.handle, packet_boundary_flag=int(pb_flag), broadcast_flag=int(b_flag), data=data)
+        self.device.hci.SendAclData(acl_msg)
+
+    def send_first(self, data):
+        self.send(hci_packets.PacketBoundaryFlag.FIRST_AUTOMATICALLY_FLUSHABLE,
+                  hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(data))
+
+    def send_continuing(self, data):
+        self.send(hci_packets.PacketBoundaryFlag.CONTINUING_FRAGMENT, hci_packets.BroadcastFlag.POINT_TO_POINT,
+                  bytes(data))
+
+    def get_event_queue(self):
+        return self.our_acl_stream.get_event_queue()
+
+
+class PyHci(Closable):
+
+    event_stream = None
+    le_event_stream = None
+    acl_stream = None
+
+    def __init__(self, device, acl_streaming=False):
+        """
+            If you are planning on personally using the ACL data stream
+            coming from HCI, specify acl_streaming=True. You probably only
+            want this if you are testing HCI itself.
+        """
+        self.device = device
+        self._setup_event_stream()
+        self._setup_le_event_stream()
+        if acl_streaming:
+            self.register_for_events(hci_packets.EventCode.ROLE_CHANGE, hci_packets.EventCode.CONNECTION_REQUEST,
+                                     hci_packets.EventCode.CONNECTION_COMPLETE,
+                                     hci_packets.EventCode.CONNECTION_PACKET_TYPE_CHANGED)
+            self._setup_acl_stream()
+
+    def _setup_event_stream(self):
+        self.event_stream = EventStream(self.device.hci.FetchEvents(empty_proto.Empty()))
+
+    def _setup_le_event_stream(self):
+        self.le_event_stream = EventStream(self.device.hci.FetchLeSubevents(empty_proto.Empty()))
+
+    def _setup_acl_stream(self):
+        self.acl_stream = EventStream(self.device.hci.FetchAclPackets(empty_proto.Empty()))
+
+    def close(self):
+        safeClose(self.event_stream)
+        safeClose(self.le_event_stream)
+        safeClose(self.acl_stream)
+
+    def get_event_stream(self):
+        return self.event_stream
+
+    def get_le_event_stream(self):
+        return self.le_event_stream
+
+    def get_raw_acl_stream(self):
+        if self.acl_stream is None:
+            raise Exception("Please construct '%s' with acl_streaming=True!" % self.__class__.__name__)
+        return self.acl_stream
+
+    def register_for_events(self, *event_codes):
+        for event_code in event_codes:
+            msg = hci_facade.EventCodeMsg(code=int(event_code))
+            self.device.hci.RegisterEventHandler(msg)
+
+    def register_for_le_events(self, *event_codes):
+        for event_code in event_codes:
+            msg = hci_facade.EventCodeMsg(code=int(event_code))
+            self.device.hci.RegisterLeEventHandler(msg)
+
+    def send_command_with_complete(self, command):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        self.device.hci.EnqueueCommandWithComplete(cmd)
+
+    def send_command_with_status(self, command):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        self.device.hci.EnqueueCommandWithStatus(cmd)
+
+    def enable_inquiry_and_page_scan(self):
+        self.send_command_with_complete(
+            hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+
+    def read_own_address(self):
+        self.send_command_with_complete(hci_packets.ReadBdAddrBuilder())
+        read_bd_addr = HciCaptures.ReadBdAddrCompleteCapture()
+        assertThat(self.event_stream).emits(read_bd_addr)
+        return read_bd_addr.get().GetBdAddr()
+
+    def initiate_connection(self, remote_addr):
+        self.send_command_with_status(
+            hci_packets.CreateConnectionBuilder(
+                remote_addr.decode('utf-8'),
+                0xcc18,  # Packet Type
+                hci_packets.PageScanRepetitionMode.R1,
+                0x0,
+                hci_packets.ClockOffsetValid.INVALID,
+                hci_packets.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH))
+
+    def accept_connection(self):
+        connection_request = HciCaptures.ConnectionRequestCapture()
+        assertThat(self.event_stream).emits(connection_request)
+
+        self.send_command_with_status(
+            hci_packets.AcceptConnectionRequestBuilder(connection_request.get().GetBdAddr(),
+                                                       hci_packets.AcceptConnectionRequestRole.REMAIN_SLAVE))
+        return self.complete_connection()
+
+    def complete_connection(self):
+        connection_complete = HciCaptures.ConnectionCompleteCapture()
+        assertThat(self.event_stream).emits(connection_complete)
+
+        handle = connection_complete.get().GetConnectionHandle()
+        if self.acl_stream is None:
+            raise Exception("Please construct '%s' with acl_streaming=True!" % self.__class__.__name__)
+        return PyHciAclConnection(handle, self.acl_stream, self.device)
diff --git a/gd/cert/py_l2cap.py b/gd/cert/py_l2cap.py
new file mode 100644
index 0000000..2eb0ca0
--- /dev/null
+++ b/gd/cert/py_l2cap.py
@@ -0,0 +1,209 @@
+#!/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 google.protobuf import empty_pb2 as empty_proto
+
+from l2cap.classic import facade_pb2 as l2cap_facade_pb2
+from l2cap.le import facade_pb2 as l2cap_le_facade_pb2
+from l2cap.le.facade_pb2 import SecurityLevel
+from bluetooth_packets_python3 import l2cap_packets
+from cert.event_stream import FilteringEventStream
+from cert.event_stream import EventStream, IEventStream
+from cert.closable import Closable, safeClose
+from cert.truth import assertThat
+from cert.matchers import L2capMatchers
+from facade import common_pb2 as common
+
+
+class PyL2capChannel(IEventStream):
+
+    def __init__(self, device, psm, l2cap_stream):
+        self._device = device
+        self._psm = psm
+        self._le_l2cap_stream = l2cap_stream
+        self._our_le_l2cap_view = FilteringEventStream(self._le_l2cap_stream,
+                                                       L2capMatchers.PacketPayloadWithMatchingPsm(self._psm))
+
+    def get_event_queue(self):
+        return self._our_le_l2cap_view.get_event_queue()
+
+    def send(self, payload):
+        self._device.l2cap.SendDynamicChannelPacket(
+            l2cap_facade_pb2.DynamicChannelPacket(psm=self._psm, payload=payload))
+
+    def close_channel(self):
+        self._device.l2cap.CloseChannel(l2cap_facade_pb2.CloseChannelRequest(psm=self._psm))
+
+    def set_traffic_paused(self, paused):
+        self._device.l2cap.SetTrafficPaused(l2cap_facade_pb2.SetTrafficPausedRequest(psm=self._psm, paused=paused))
+
+
+class _ClassicConnectionResponseFutureWrapper(object):
+    """
+    The future object returned when we send a connection request from DUT. Can be used to get connection status and
+    create the corresponding PyL2capDynamicChannel object later
+    """
+
+    def __init__(self, grpc_response_future, device, psm, l2cap_stream):
+        self._grpc_response_future = grpc_response_future
+        self._device = device
+        self._psm = psm
+        self._l2cap_stream = l2cap_stream
+
+    def get_channel(self):
+        return PyL2capChannel(self._device, self._psm, self._l2cap_stream)
+
+
+class PyL2cap(Closable):
+
+    def __init__(self, device, cert_address):
+        self._device = device
+        self._cert_address = cert_address
+        self._l2cap_stream = EventStream(self._device.l2cap.FetchL2capData(empty_proto.Empty()))
+
+    def close(self):
+        safeClose(self._l2cap_stream)
+
+    def register_dynamic_channel(self, psm=0x33, mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC):
+        self._device.l2cap.SetDynamicChannel(
+            l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=mode))
+        return PyL2capChannel(self._device, psm, self._l2cap_stream)
+
+    def connect_dynamic_channel_to_cert(self, psm=0x33, mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC):
+        """
+        Send open Dynamic channel request to CERT.
+        Get a future for connection result, to be used after CERT accepts request
+        """
+        self.register_dynamic_channel(psm, mode)
+        response_future = self._device.l2cap.OpenChannel.future(
+            l2cap_facade_pb2.OpenChannelRequest(psm=psm, remote=self._cert_address, mode=mode))
+
+        return _ClassicConnectionResponseFutureWrapper(response_future, self._device, psm, self._l2cap_stream)
+
+    def get_channel_queue_buffer_size(self):
+        return self._device.l2cap.GetChannelQueueDepth(empty_proto.Empty()).size
+
+
+class PyLeL2capFixedChannel(IEventStream):
+
+    def __init__(self, device, cid, l2cap_stream):
+        self._device = device
+        self._cid = cid
+        self._le_l2cap_stream = l2cap_stream
+        self._our_le_l2cap_view = FilteringEventStream(self._le_l2cap_stream,
+                                                       L2capMatchers.PacketPayloadWithMatchingCid(self._cid))
+
+    def get_event_queue(self):
+        return self._our_le_l2cap_view.get_event_queue()
+
+    def send(self, payload):
+        self._device.l2cap_le.SendFixedChannelPacket(
+            l2cap_le_facade_pb2.FixedChannelPacket(cid=self._cid, payload=payload))
+
+    def close_channel(self):
+        self._device.l2cap_le.SetFixedChannel(
+            l2cap_le_facade_pb2.SetEnableFixedChannelRequest(cid=self._cid, enable=False))
+
+
+class PyLeL2capDynamicChannel(IEventStream):
+
+    def __init__(self, device, cert_address, psm, l2cap_stream):
+        self._device = device
+        self._cert_address = cert_address
+        self._psm = psm
+        self._le_l2cap_stream = l2cap_stream
+        self._our_le_l2cap_view = FilteringEventStream(self._le_l2cap_stream,
+                                                       L2capMatchers.PacketPayloadWithMatchingPsm(self._psm))
+
+    def get_event_queue(self):
+        return self._our_le_l2cap_view.get_event_queue()
+
+    def send(self, payload):
+        self._device.l2cap_le.SendDynamicChannelPacket(
+            l2cap_le_facade_pb2.DynamicChannelPacket(psm=self._psm, payload=payload))
+
+    def close_channel(self):
+        self._device.l2cap_le.CloseDynamicChannel(
+            l2cap_le_facade_pb2.CloseDynamicChannelRequest(remote=self._cert_address, psm=self._psm))
+
+
+class _CreditBasedConnectionResponseFutureWrapper(object):
+    """
+    The future object returned when we send a connection request from DUT. Can be used to get connection status and
+    create the corresponding PyLeL2capDynamicChannel object later
+    """
+
+    def __init__(self, grpc_response_future, device, cert_address, psm, le_l2cap_stream):
+        self._grpc_response_future = grpc_response_future
+        self._device = device
+        self._cert_address = cert_address
+        self._psm = psm
+        self._le_l2cap_stream = le_l2cap_stream
+
+    def get_status(self):
+        return l2cap_packets.LeCreditBasedConnectionResponseResult(self._grpc_response_future.result().status)
+
+    def get_channel(self):
+        assertThat(self.get_status()).isEqualTo(l2cap_packets.LeCreditBasedConnectionResponseResult.SUCCESS)
+        return PyLeL2capDynamicChannel(self._device, self._cert_address, self._psm, self._le_l2cap_stream)
+
+
+class PyLeL2cap(Closable):
+
+    def __init__(self, device):
+        self._device = device
+        self._le_l2cap_stream = EventStream(self._device.l2cap_le.FetchL2capData(empty_proto.Empty()))
+
+    def close(self):
+        safeClose(self._le_l2cap_stream)
+
+    def enable_fixed_channel(self, cid=4):
+        self._device.l2cap_le.SetFixedChannel(l2cap_le_facade_pb2.SetEnableFixedChannelRequest(cid=cid, enable=True))
+
+    def get_fixed_channel(self, cid=4):
+        return PyLeL2capFixedChannel(self._device, cid, self._le_l2cap_stream)
+
+    def register_coc(self, cert_address, psm=0x33, security_level=SecurityLevel.NO_SECURITY):
+        self._device.l2cap_le.SetDynamicChannel(
+            l2cap_le_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, enable=True, security_level=security_level))
+        return PyLeL2capDynamicChannel(self._device, cert_address, psm, self._le_l2cap_stream)
+
+    def connect_coc_to_cert(self, cert_address, psm=0x33):
+        """
+        Send open LE COC request to CERT. Get a future for connection result, to be used after CERT accepts request
+        """
+        self.register_coc(cert_address, psm)
+        response_future = self._device.l2cap_le.OpenDynamicChannel.future(
+            l2cap_le_facade_pb2.OpenDynamicChannelRequest(psm=psm, remote=cert_address))
+
+        return _CreditBasedConnectionResponseFutureWrapper(response_future, self._device, cert_address, psm,
+                                                           self._le_l2cap_stream)
+
+    def update_connection_parameter(self,
+                                    conn_interval_min=0x10,
+                                    conn_interval_max=0x10,
+                                    conn_latency=0x0a,
+                                    supervision_timeout=0x64,
+                                    min_ce_length=12,
+                                    max_ce_length=12):
+        self._device.l2cap_le.SendConnectionParameterUpdate(
+            l2cap_le_facade_pb2.ConnectionParameter(
+                conn_interval_min=conn_interval_min,
+                conn_interval_max=conn_interval_max,
+                conn_latency=conn_latency,
+                supervision_timeout=supervision_timeout,
+                min_ce_length=min_ce_length,
+                max_ce_length=max_ce_length))
diff --git a/gd/cert/py_le_acl_manager.py b/gd/cert/py_le_acl_manager.py
new file mode 100644
index 0000000..e6cc8ee
--- /dev/null
+++ b/gd/cert/py_le_acl_manager.py
@@ -0,0 +1,132 @@
+#!/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 google.protobuf import empty_pb2 as empty_proto
+
+from cert.event_stream import EventStream
+from cert.event_stream import IEventStream
+from cert.captures import HciCaptures
+from cert.closable import Closable
+from cert.closable import safeClose
+from bluetooth_packets_python3 import hci_packets
+from cert.truth import assertThat
+from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
+
+
+class PyLeAclManagerAclConnection(IEventStream, Closable):
+
+    def __init__(self, le_acl_manager, address, remote_addr, handle, event_stream):
+        """
+        An abstract representation for an LE ACL connection in GD certification test
+        :param le_acl_manager: The LeAclManager from this GD device
+        :param address: The local device address
+        :param remote_addr: Remote device address
+        :param handle: Connection handle
+        :param event_stream: The connection event stream for this connection
+        """
+        self.le_acl_manager = le_acl_manager
+        # todo enable filtering after sorting out handles
+        # self.our_acl_stream = FilteringEventStream(acl_stream, None)
+        self.handle = handle
+        self.connection_event_stream = event_stream
+        self.acl_stream = EventStream(
+            self.le_acl_manager.FetchAclData(le_acl_manager_facade.LeHandleMsg(handle=self.handle)))
+        self.remote_address = remote_addr
+        self.own_address = address
+        self.disconnect_reason = None
+
+    def close(self):
+        safeClose(self.connection_event_stream)
+        safeClose(self.acl_stream)
+
+    def wait_for_disconnection_complete(self):
+        disconnection_complete = HciCaptures.DisconnectionCompleteCapture()
+        assertThat(self.connection_event_stream).emits(disconnection_complete)
+        self.disconnect_reason = disconnection_complete.get().GetReason()
+
+    def send(self, data):
+        self.le_acl_manager.SendAclData(le_acl_manager_facade.LeAclData(handle=self.handle, payload=bytes(data)))
+
+    def get_event_queue(self):
+        return self.acl_stream.get_event_queue()
+
+
+class PyLeAclManager(Closable):
+
+    def __init__(self, device):
+        """
+        LE ACL Manager for GD Certification test
+        :param device: The GD device
+        """
+        self.le_acl_manager = device.hci_le_acl_manager
+
+        self.incoming_connection_event_stream = None
+        self.outgoing_connection_event_streams = {}
+        self.active_connections = []
+        self.next_token = 1
+
+    def close(self):
+        safeClose(self.incoming_connection_event_stream)
+        for v in self.outgoing_connection_event_streams.values():
+            safeClose(v)
+        for connection in self.active_connections:
+            safeClose(connection)
+
+    def listen_for_incoming_connections(self):
+        assertThat(self.incoming_connection_event_stream).isNone()
+        self.incoming_connection_event_stream = EventStream(
+            self.le_acl_manager.FetchIncomingConnection(empty_proto.Empty()))
+
+    def connect_to_remote(self, remote_addr):
+        token = self.initiate_connection(remote_addr)
+        return self.complete_outgoing_connection(token)
+
+    def wait_for_connection(self):
+        self.listen_for_incoming_connections()
+        return self.complete_incoming_connection()
+
+    def initiate_connection(self, remote_addr):
+        assertThat(self.next_token in self.outgoing_connection_event_streams).isFalse()
+        self.outgoing_connection_event_streams[self.next_token] = EventStream(
+            self.le_acl_manager.CreateConnection(remote_addr))
+        token = self.next_token
+        self.next_token += 1
+        return token
+
+    def complete_connection(self, event_stream):
+        connection_complete = HciCaptures.LeConnectionCompleteCapture()
+        assertThat(event_stream).emits(connection_complete)
+        complete = connection_complete.get()
+        handle = complete.GetConnectionHandle()
+        remote = complete.GetPeerAddress()
+        if complete.GetSubeventCode() == hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE:
+            address = complete.GetLocalResolvablePrivateAddress()
+        else:
+            address = None
+        connection = PyLeAclManagerAclConnection(self.le_acl_manager, address, remote, handle, event_stream)
+        self.active_connections.append(connection)
+        return connection
+
+    def complete_incoming_connection(self):
+        assertThat(self.incoming_connection_event_stream).isNotNone()
+        event_stream = self.incoming_connection_event_stream
+        self.incoming_connection_event_stream = None
+        return self.complete_connection(event_stream)
+
+    def complete_outgoing_connection(self, token):
+        assertThat(self.outgoing_connection_event_streams[token]).isNotNone()
+        event_stream = self.outgoing_connection_event_streams.pop(token)
+        return self.complete_connection(event_stream)
diff --git a/gd/cert/py_le_security.py b/gd/cert/py_le_security.py
new file mode 100644
index 0000000..67e4be0
--- /dev/null
+++ b/gd/cert/py_le_security.py
@@ -0,0 +1,75 @@
+#!/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 logging
+
+from bluetooth_packets_python3 import hci_packets
+from cert.captures import SecurityCaptures
+from cert.closable import Closable
+from cert.closable import safeClose
+from cert.event_stream import EventStream
+from cert.truth import assertThat
+from datetime import timedelta
+from facade import common_pb2 as common
+from google.protobuf import empty_pb2 as empty_proto
+from hci.facade import facade_pb2 as hci_facade
+from security.facade_pb2 import IoCapabilityMessage
+from security.facade_pb2 import AuthenticationRequirementsMessage
+from security.facade_pb2 import LeAuthRequirementsMessage
+from security.facade_pb2 import OobDataMessage
+from security.facade_pb2 import UiCallbackMsg
+from security.facade_pb2 import UiCallbackType
+
+
+class PyLeSecurity(Closable):
+    """
+        Abstraction for security tasks and GRPC calls
+    """
+
+    _ui_event_stream = None
+    _bond_event_stream = None
+
+    def __init__(self, device):
+        logging.info("DUT: Init")
+        self._device = device
+        self._device.wait_channel_ready()
+        self._ui_event_stream = EventStream(self._device.security.FetchUiEvents(empty_proto.Empty()))
+        self._bond_event_stream = EventStream(self._device.security.FetchBondEvents(empty_proto.Empty()))
+
+    def get_ui_stream(self):
+        return self._ui_event_stream
+
+    def get_bond_stream(self):
+        return self._bond_event_stream
+
+    def wait_for_ui_event_passkey(self, timeout=timedelta(seconds=3)):
+        display_passkey_capture = SecurityCaptures.DisplayPasskey()
+        assertThat(self._ui_event_stream).emits(display_passkey_capture, timeout=timeout)
+        return display_passkey_capture.get()
+
+    def SetLeAuthRequirements(self, *args, **kwargs):
+        return self._device.security.SetLeAuthRequirements(LeAuthRequirementsMessage(*args, **kwargs))
+
+    def close(self):
+        if self._ui_event_stream is not None:
+            safeClose(self._ui_event_stream)
+        else:
+            logging.info("DUT: UI Event Stream is None!")
+
+        if self._bond_event_stream is not None:
+            safeClose(self._bond_event_stream)
+        else:
+            logging.info("DUT: Bond Event Stream is None!")
diff --git a/gd/cert/py_security.py b/gd/cert/py_security.py
new file mode 100644
index 0000000..56b1753
--- /dev/null
+++ b/gd/cert/py_security.py
@@ -0,0 +1,196 @@
+#!/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 logging
+
+from bluetooth_packets_python3 import hci_packets
+from cert.closable import Closable
+from cert.closable import safeClose
+from cert.event_stream import EventStream
+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 facade_pb2 as hci_facade
+
+from security.facade_pb2 import AuthenticationRequirements
+from security.facade_pb2 import AuthenticationRequirementsMessage
+from security.facade_pb2 import SecurityPolicyMessage
+from security.facade_pb2 import IoCapabilities
+from security.facade_pb2 import IoCapabilityMessage
+from security.facade_pb2 import OobDataMessage
+from security.facade_pb2 import OobDataPresent
+from security.facade_pb2 import UiCallbackMsg
+from security.facade_pb2 import UiCallbackType
+
+
+class PySecurity(Closable):
+    """
+        Abstraction for security tasks and GRPC calls
+    """
+
+    _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",
+    }
+
+    _ui_event_stream = None
+    _bond_event_stream = None
+
+    def __init__(self, device):
+        logging.debug("DUT: Init")
+        self._device = device
+        self._device.wait_channel_ready()
+        self._ui_event_stream = EventStream(self._device.security.FetchUiEvents(empty_proto.Empty()))
+        self._bond_event_stream = EventStream(self._device.security.FetchBondEvents(empty_proto.Empty()))
+        self._enforce_security_policy_stream = EventStream(
+            self._device.security.FetchEnforceSecurityPolicyEvents(empty_proto.Empty()))
+
+    def create_bond(self, address, type):
+        """
+            Triggers stack under test to create bond
+        """
+        logging.debug("DUT: Creating bond to '%s' from '%s'" % (str(address), str(self._device.address)))
+        self._device.security.CreateBond(
+            common.BluetoothAddressWithType(address=common.BluetoothAddress(address=address), type=type))
+
+    def remove_bond(self, address, type):
+        """
+            Removes bond from stack under test
+        """
+        self._device.security.RemoveBond(
+            common.BluetoothAddressWithType(address=common.BluetoothAddress(address=address), type=type))
+
+    def set_io_capabilities(self, io_capabilities):
+        """
+            Set the IO Capabilities used for the DUT
+        """
+        logging.debug("DUT: setting IO Capabilities data to '%s'" % self._io_capabilities_name_lookup.get(
+            io_capabilities, "ERROR"))
+        self._device.security.SetIoCapability(IoCapabilityMessage(capability=io_capabilities))
+
+    def set_authentication_requirements(self, auth_reqs):
+        """
+            Establish authentication requirements for the stack
+        """
+        logging.debug("DUT: setting Authentication Requirements data to '%s'" % self._auth_reqs_name_lookup.get(
+            auth_reqs, "ERROR"))
+        self._device.security.SetAuthenticationRequirements(AuthenticationRequirementsMessage(requirement=auth_reqs))
+
+    def set_oob_data(self, data_present):
+        """
+            Set the Out-of-band data present flag for SSP pairing
+        """
+        logging.debug("DUT: setting OOB data present to '%s'" % data_present)
+        self._device.security.SetOobDataPresent(OobDataMessage(data_present=data_present))
+
+    def send_ui_callback(self, address, callback_type, b, uid):
+        """
+            Send a callback from the UI as if the user pressed a button on the dialog
+        """
+        logging.debug("DUT: Sending user input response uid: %d; response: %s" % (uid, b))
+        self._device.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=callback_type,
+                boolean=b,
+                unique_id=uid,
+                address=common.BluetoothAddressWithType(
+                    address=common.BluetoothAddress(address=address),
+                    type=common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)))
+
+    def enable_secure_simple_pairing(self):
+        """
+            This is called when you want to enable SSP for testing
+            Since the stack under test already enables it by default
+            we do not need to do anything here for the time being
+        """
+        pass
+
+    def accept_pairing(self, cert_address, reply_boolean):
+        """
+            Here we pass, but in cert we perform pairing flow tasks.
+            This was added here in order to be more dynamic, but the stack
+            under test will handle the pairing flow.
+        """
+        pass
+
+    def on_user_input(self, cert_address, reply_boolean, expected_ui_event):
+        """
+            Respond to the UI event
+        """
+        if expected_ui_event is None:
+            return
+
+        ui_id = -1
+
+        def get_unique_id(event):
+            if event.message_type == expected_ui_event:
+                nonlocal ui_id
+                ui_id = event.unique_id
+                return True
+            return False
+
+        logging.debug("DUT: Waiting for expected UI event")
+        assertThat(self._ui_event_stream).emits(get_unique_id)
+        # TODO(optedoblivion): Make UiCallbackType dynamic for PASSKEY when added
+        self.send_ui_callback(cert_address, UiCallbackType.YES_NO, reply_boolean, ui_id)
+
+    def get_address(self):
+        return self._device.address
+
+    def wait_for_bond_event(self, expected_bond_event):
+        """
+            A bond event will be triggered once the bond process
+            is complete.  For the DUT we need to wait for it,
+            for Cert it isn't needed.
+        """
+        logging.debug("DUT: Waiting for Bond Event")
+        assertThat(self._bond_event_stream).emits(lambda event: event.message_type == expected_bond_event)
+
+    def wait_for_enforce_security_event(self, expected_enforce_security_event):
+        """
+            We expect a 'True' or 'False' from the enforce security call
+
+            This interface will allow the caller to wait for a callback
+            result from enforcing security policy over the facade.
+        """
+        logging.info("DUT: Waiting for enforce security event")
+        assertThat(self._enforce_security_policy_stream).emits(
+            lambda event: event.result == expected_enforce_security_event or logging.info(event.result))
+
+    def enforce_security_policy(self, address, type, policy):
+        """
+            Call to enforce classic security policy
+        """
+        self._device.security.EnforceSecurityPolicy(
+            SecurityPolicyMessage(
+                address=common.BluetoothAddressWithType(address=common.BluetoothAddress(address=address), type=type),
+                policy=policy))
+
+    def close(self):
+        safeClose(self._ui_event_stream)
+        safeClose(self._bond_event_stream)
+        safeClose(self._enforce_security_policy_stream)
diff --git a/gd/cert/read_only_property_server.cc b/gd/cert/read_only_property_server.cc
deleted file mode 100644
index d67c33e..0000000
--- a/gd/cert/read_only_property_server.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "cert/read_only_property_server.h"
-#include "hci/controller.h"
-
-namespace bluetooth {
-namespace cert {
-
-class ReadOnlyPropertyService : public ReadOnlyProperty::Service {
- public:
-  ReadOnlyPropertyService(hci::Controller* controller) : controller_(controller) {}
-  ::grpc::Status ReadLocalAddress(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                  ::bluetooth::facade::BluetoothAddress* response) override {
-    auto address = controller_->GetControllerMacAddress().ToString();
-    response->set_address(address);
-    return ::grpc::Status::OK;
-  }
-
- private:
-  hci::Controller* controller_;
-};
-
-void ReadOnlyPropertyServerModule::ListDependencies(ModuleList* list) {
-  GrpcFacadeModule::ListDependencies(list);
-  list->add<hci::Controller>();
-}
-void ReadOnlyPropertyServerModule::Start() {
-  GrpcFacadeModule::Start();
-  service_ = std::make_unique<ReadOnlyPropertyService>(GetDependency<hci::Controller>());
-}
-void ReadOnlyPropertyServerModule::Stop() {
-  service_.reset();
-  GrpcFacadeModule::Stop();
-}
-::grpc::Service* ReadOnlyPropertyServerModule::GetService() const {
-  return service_.get();
-}
-
-const ModuleFactory ReadOnlyPropertyServerModule::Factory =
-    ::bluetooth::ModuleFactory([]() { return new ReadOnlyPropertyServerModule(); });
-
-}  // namespace cert
-}  // namespace bluetooth
diff --git a/gd/cert/read_only_property_server.h b/gd/cert/read_only_property_server.h
deleted file mode 100644
index e367537..0000000
--- a/gd/cert/read_only_property_server.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-
-#include <grpc++/grpc++.h>
-
-#include "cert/rootservice.grpc.pb.h"
-#include "grpc/grpc_module.h"
-
-namespace bluetooth {
-namespace cert {
-
-class ReadOnlyPropertyService;
-
-class ReadOnlyPropertyServerModule : public ::bluetooth::grpc::GrpcFacadeModule {
- public:
-  static const ModuleFactory Factory;
-
-  void ListDependencies(ModuleList* list) override;
-  void Start() override;
-  void Stop() override;
-  ::grpc::Service* GetService() const override;
-
- private:
-  std::unique_ptr<ReadOnlyPropertyService> service_;
-};
-
-}  // namespace cert
-}  // namespace bluetooth
diff --git a/gd/cert/rootservice.proto b/gd/cert/rootservice.proto
deleted file mode 100644
index 4472bfe..0000000
--- a/gd/cert/rootservice.proto
+++ /dev/null
@@ -1,32 +0,0 @@
-syntax = "proto3";
-
-package bluetooth.cert;
-
-import "google/protobuf/empty.proto";
-import "facade/common.proto";
-
-service RootCert {
-  rpc StartStack(StartStackRequest) returns (StartStackResponse) {}
-  rpc StopStack(StopStackRequest) returns (StopStackResponse) {}
-}
-
-enum BluetoothModule {
-  HAL = 0;
-  HCI = 1;
-  L2CAP = 2;
-  SECURITY = 3;
-}
-
-message StartStackRequest {
-  BluetoothModule module_to_test = 1;
-}
-
-message StartStackResponse {}
-
-message StopStackRequest {}
-
-message StopStackResponse {}
-
-service ReadOnlyProperty {
-  rpc ReadLocalAddress(google.protobuf.Empty) returns (facade.BluetoothAddress) {}
-}
diff --git a/gd/cert/run b/gd/cert/run
new file mode 100755
index 0000000..de8f6cc
--- /dev/null
+++ b/gd/cert/run
@@ -0,0 +1,329 @@
+#! /bin/bash
+
+YELLOW="\033[1;33m"
+NOCOLOR="\033[0m"
+BLUE="\033[1;34m"
+RED="\033[1;91m"
+
+function happy_hedgehog {
+    echo -e "\t${BLUE}"
+    echo -e "\t       ___------__"
+    echo -e "\t |\__-- /\       _-"
+    echo -e "\t |/_   __      -"
+    echo -e "\t // \ /  \    /__"
+    echo -e "\t | 0 |  0 |__     --_        Gotta go fast!"
+    echo -e "\t \\____-- __ \   ___-"
+    echo -e "\t ( @    __/  / /_"
+    echo -e "\t    -_____---   --_"
+    echo -e "\t     //  \ \\   ___-"
+    echo -e "\t   //|\__/  \\  \\"
+    echo -e "\t   \_-\_____/  \-\\"
+    echo -e "\t        // \\--\|"
+    echo -e "\t   ${RED}____${BLUE}//  ||${RED}_"
+    echo -e "\t${RED}  /_____\ /___\\"
+    echo -e "${NOCOLOR}"
+}
+
+function sad_hedgehog {
+    echo -e "\t${BLUE}"
+    echo -e "\t       ___------__"
+    echo -e "\t |\__-- /\       _-"
+    echo -e "\t |/_    __      -"
+    echo -e "\t // \  /  \    /__"
+    echo -e "\t | 0 |  0 |__     --_        Gotta go sllloowwww!"
+    echo -e "\t \\____-- __ \   ___-"
+    echo -e "\t ( @    __   / /_"
+    echo -e "\t    -_____---   --_"
+    echo -e "\t     //  \ \\   ___-"
+    echo -e "\t   //|\__/  \\  \\"
+    echo -e "\t   \_-\_____/  \-\\"
+    echo -e "\t        // \\--\|"
+    echo -e "\t  ${RED} ____${BLUE}//  ||${RED}_"
+    echo -e "\t${RED}  /_____\ /___\\"
+    echo -e "{$NOCOLOR}"
+}
+
+function check_environment {
+    if [[ -z "${ANDROID_BUILD_TOP}" ]] || [[ -z "${ANDROID_HOST_OUT}" ]] ; then
+      echo -e "${RED}ANDROID_BUILD_TOP${NOCOLOR} or ${RED}ANDROID_HOST_OUT${NOCOLOR} is not set for host run"
+      echo -e "Navigate to android root and run:"
+      echo -e "${YELLOW}"
+      echo -e ". build/envsetup.sh"
+      echo -e "lunch <fish>"
+      echo -e "${NOCOLOR}"
+      echo
+      exit 1
+    fi
+    if ! [ -x "$(command -v python3.8)" ] ; then
+      echo -e "${RED}You must have python 3.8 installed${NOCOLOR}"
+      exit 1
+    fi
+}
+
+ASHMEM_OUT="/dev/shm/out"
+ASHMEM_DIST="${ASHMEM_OUT}/dist"
+ASHMEM_VENV="${ASHMEM_DIST}/bluetooth_venv"
+ASHMEM_GOTTA_GO_FAST="/dev/shm/gottagofast"
+ASHMEM_HOST_LOGS="${ASHMEM_GOTTA_GO_FAST}/logs"
+ASHMEM_OUT_TARGET="${ASHMEM_GOTTA_GO_FAST}/target"
+ASHMEM_SOONG="${ASHMEM_GOTTA_GO_FAST}/out/soong"
+CERT_HOST_LOGS="/tmp/logs/HostOnlyCert"
+CERT_DEVICE_LOGS="TODO: Add this"
+CERT_TEST_VENV=${ANDROID_BUILD_TOP}/out/dist/bluetooth_venv
+OUT_TARGET="${ANDROID_BUILD_TOP}/out/target"
+TEST_CONFIG="${ANDROID_BUILD_TOP}/system/bt/gd/cert/android_devices_config.json"
+TEST_FILTER="-tf ${ANDROID_BUILD_TOP}/system/bt/gd/cert/all_cert_testcases"
+
+CLEAN_VENV=false
+GOTTA_GO_FAST=false
+NUM_REPETITIONS="1"
+SKIP_SOONG_BUILD=false
+USE_ASHMEM_VENV=true
+VERBOSE_MODE=false
+
+# Used for position arguments needed for later
+POSITIONAL=()
+function parse_options {
+    while [[ $# -gt 0 ]]
+    do
+    key="$1"
+    case $key in
+        # This will delete the existing venv before running the test
+        # If you updated external libraries such as ACTS, you need to add this flag
+        --clean)
+        CLEAN_VENV=true
+        shift # past argument
+        ;;
+        --help)
+        echo
+        echo -e "${YELLOW}Help menu${NOCOLOR}"
+        echo -e "==================================="
+        echo -e "${BLUE}  --clean${NOCOLOR}" 
+        echo -e "    Clean the virtul environment; use if ACTS has been updated." 
+        echo -e "${BLUE}  --disable-ashmem-venv${NOCOLOR}" 
+        echo -e "    Places the virtual environment on disk rather than in ashmem which is default." 
+        echo -e "${BLUE}  --gotta-go-fast${NOCOLOR}" 
+        echo -e "    Makes use of ashmem as best as possible for targeted speed increases." 
+        echo -e "${BLUE}  --host${NOCOLOR}" 
+        echo -e "    Run the test on the host machine [i.e. simulated]." 
+        echo -e "${BLUE}  --repeat=<N>${NOCOLOR}" 
+        echo -e "    Repeat the test sequence N (int) number of times." 
+        echo -e "${BLUE}  --skip-soong-build${NOCOLOR}" 
+        echo -e "    Skips building soong targets. Use when you are just modifying simple python files." 
+        echo -e "${BLUE}  --test_config=<configfile>${NOCOLOR}" 
+        echo -e "    Override default test configuration." 
+        echo -e "${BLUE}  --verbose${NOCOLOR}"
+        echo -e "    Displays device logs and test logs to output." 
+        echo
+        echo -e "Usage: $0 [--clean|--host|--repeat=<N>|--test_config=<config>] [TestGroupName[:IndividualTestName]]"
+        echo -e "        ${YELLOW}e.g."
+        echo -e "         $0 --host --clean SecurityTest"
+        echo -e "         $0 --host --verbose SecurityTest:test_dut_initiated_display_only_display_only ${NOCOLOR}"
+        echo
+        shift
+        exit 0
+        ;;
+        # This will cause the bluetooth_venv to be created in ashmem
+        # Increases --clean build times by 40% (~21 seconds on my machine)
+        --disable-ashmem-venv)
+        USE_ASHMEM_VENV=false
+        shift # past argument
+        ;;
+        --gotta-go-fast)
+        GOTTA_GO_FAST=true
+        shift # past argument
+        ;;
+        --host)
+        TEST_CONFIG=$ANDROID_BUILD_TOP/system/bt/gd/cert/host_config.json
+        shift # past argument
+        ;;
+        # Repeat running the specified test cases by N times in one single setup
+        --repeat=*)
+        NUM_REPETITIONS="${key#*=}"
+        shift # past argument
+        ;;
+        --skip-soong-build)
+        SKIP_SOONG_BUILD=true
+        shift
+        ;;
+        --test_config=*)
+        TEST_CONFIG="${key#*=}"
+        shift # past argument
+        ;;
+        # This will log everything to both log file and stdout
+        --verbose)
+        VERBOSE_MODE=true
+        shift # past argument
+        ;;
+        *)    # unknown option
+        POSITIONAL+=("$1") # save it in an array for later
+        shift # past argument
+        ;;
+    esac
+    done
+    set -- "${POSITIONAL[@]}" # restore positional parameters
+
+    # Set the test filter
+    if [[ -n "$1" ]] ; then
+      TEST_FILTER="-tc $1"
+    fi
+    
+    INSTALL_ARGS="--reuse-acts"
+    if [ "$CLEAN_VENV" == true ] ; then
+      echo -e "${YELLOW}Cleaning up existing virtualenv${NOCOLOR}"
+      rm -rf $CERT_TEST_VENV/*
+      rm -rf $CERT_TEST_VENV
+      mkdir -p ${CERT_TEST_VENV}
+      INSTALL_ARGS=""
+    else
+      echo -e "${YELLOW}Try to reuse existing virtualenv at ${CERT_TEST_VENV}${NOCOLOR}"
+    fi
+
+}
+
+function soong_build {
+    $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="${ANDROID_BUILD_TOP}/system/bt/gd" dist bluetooth_stack_with_facade
+    if [[ $? -ne 0 ]] ; then
+        echo "Failed to build bluetooth_stack_with_facade"
+        exit 1
+    fi
+}
+
+function setup_venv {
+    # Make venv in memory, decreases --clean build times by 40%
+    # Caveat is you lose the venv if the computer reboots
+    if [ "${USE_ASHMEM_VENV}" == true ] ; then
+        echo -e "${BLUE}Using ashmem virtual environment.${NOCOLOR}"
+        if [[ ! -L ${CERT_TEST_VENV} ]] ; then
+            echo -e "${BLUE}"
+            echo -ne "Creating ashmem dist folder..."
+            mkdir -p "${ASHMEM_VENV}"
+            # Ensure the directory doesn't exist
+            rm -rf "${CERT_TEST_VENV}"
+            echo -e "Done"
+            echo -ne "Sym linking ${ASHMEM_VENV} to ${CERT_TEST_VENV}..."
+            ln -s "${ASHMEM_VENV}" "${CERT_TEST_VENV}"
+            echo -e "Done"
+            echo -e "${NOCOLOR}"
+        fi
+    else
+        echo -e "${RED}Not using ashmem virtual environment.${NOCOLOR}"
+        if [[ -L ${CERT_TEST_VENV} ]] ; then
+            echo -e "${RED}"
+            echo -en "Removing sym link from ${ASHMEM_VENV} to ${CERT_TEST_VENV}..."
+            rm -rf ""${ASHMEM_VENV} "${CERT_TEST_VENV}"
+            echo -e "Done"
+            echo -en "Cleaning up memory..."
+            rm -rf "${ASHMEM_VENV}" 
+            echo -e "Done"
+            echo -e "${NOCOLOR}"
+        fi
+    fi
+    python3.8 -m virtualenv --python `which python3.8` "${CERT_TEST_VENV}"
+    if [[ $? -ne 0 ]] ; then
+        echo "Error setting up virtualenv"
+        exit 1
+    fi
+    
+    unzip -o -q "${ANDROID_BUILD_TOP}/out/dist/bluetooth_cert_tests.zip" -d "${CERT_TEST_VENV}/acts"
+    if [[ $? -ne 0 ]] ; then
+        echo "Error unzipping bluetooth_cert_tests.zip"
+        exit 1
+    fi
+    
+    $(echo "${CERT_TEST_VENV}/bin/python" "${CERT_TEST_VENV}/acts/setup.py" --quiet build --force)
+    if [[ $? -ne 0 ]] ; then
+        echo "Error building GD Python libraries"
+        exit 1
+    fi
+    
+    $(echo "${CERT_TEST_VENV}/bin/python" "${CERT_TEST_VENV}/acts/setup.py" --quiet install --skip-build --force "${INSTALL_ARGS}")
+    if [[ $? -ne 0 ]] ; then
+        echo "Error installing GD Python libraries"
+        exit 1
+    fi
+
+"${CERT_TEST_VENV}/bin/python" -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
+if [[ $? -ne 0 ]] ; then
+  echo "Setup failed as bluetooth_packets_python3 cannot be imported"
+  exit 1
+fi
+
+if [ "${VERBOSE_MODE}" == true ] ; then
+  TEMP_CONFIG=/tmp/temp_acts_config.json
+  cat "${TEST_CONFIG}" | "${CERT_TEST_VENV}/bin/python" -c "
+import sys
+import json
+from acts import keys
+config = json.load(sys.stdin)
+config['verbose_mode'] = True
+print(json.dumps(config))
+  " > "${TEMP_CONFIG}"
+  TEST_CONFIG="${TEMP_CONFIG}"
+  if [[ $? -ne 0 ]] ; then
+    echo "Setup failed as verbose mode is chosen but cannot be enabled"
+    exit 1
+  fi
+fi
+
+}
+
+function gotta_go_fast {
+    if [ "${GOTTA_GO_FAST}" == true ] ; then
+        # Call here to explicitly note the flag is in use
+        happy_hedgehog
+        if [[ ! -L "${CERT_HOST_LOGS}" ]] ; then
+            rm -rf "${CERT_HOST_LOGS}"
+            mkdir -p "${ASHMEM_HOST_LOGS}"
+            ln -s "${ASHMEM_HOST_LOGS}" "${CERT_HOST_LOGS}" 
+        fi
+
+        if [[ ! -L "${OUT_TARGET}" ]] ; then
+            rm -rf "${OUT_TARGET}"
+            mkdir -p "${ASHMEM_OUT_TARGET}"
+            ln -s  "${ASHMEM_OUT_TARGET}" "${OUT_TARGET}"
+        fi
+    else
+        if [[ -L "${CERT_HOST_LOGS}" ]] ; then
+            # Call here so we don't spam anyone not using the flag
+            sad_hedgehog
+            rm -rf "${CERT_HOST_LOGS}"
+            rm -rf "${ASHMEM_HOST_LOGS}"
+        fi
+
+        if [[ -L "${OUT_TARGET}" ]] ; then
+            rm -rf "${OUT_TARGET}"
+            rm -rf "${ASHMEM_OUT_TARGET}"
+        fi
+    fi
+}
+
+function run_tests {
+    for n in $(seq "${NUM_REPETITIONS}"); do
+      $(echo "${CERT_TEST_VENV}/bin/python" "${CERT_TEST_VENV}/bin/act.py" \
+          -c "${TEST_CONFIG}" \
+          "${TEST_FILTER}" \
+          -tp "${CERT_TEST_VENV}"/acts)
+    done
+    
+    if [ "${CLEAN_VENV}" != true ] ; then
+      echo -e "${YELLOW}NOTE:${NOCOLOR} Completed tests using existing external libraries in virtualenv."
+      echo -e "${YELLOW}NOTE:${NOCOLOR} To update external libraries, please add --clean option."
+    fi
+}
+
+function main {
+    check_environment
+    parse_options $@
+    if [[ "${SKIP_SOONG_BUILD}" != true ]] ; then
+        soong_build
+    fi
+    setup_venv
+    gotta_go_fast
+    run_tests
+}
+
+main $@
diff --git a/gd/cert/run_cert.sh b/gd/cert/run_cert.sh
deleted file mode 100755
index a85b994..0000000
--- a/gd/cert/run_cert.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /bin/bash
-
-# For bluetooth_packets_python3
-export PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64
-python3.8 `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/cert/host_only_config.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/cert_testcases -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/cert/run_device_cert.sh b/gd/cert/run_device_cert.sh
deleted file mode 100755
index 7566f65..0000000
--- a/gd/cert/run_device_cert.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /bin/bash
-
-# For bluetooth_packets_python3
-export PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64
-python3.8 `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/cert/android_devices_config.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/cert_testcases -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/cert/run_pts_l2cap.sh b/gd/cert/run_pts_l2cap.sh
index 4539c65..356b31f 100755
--- a/gd/cert/run_pts_l2cap.sh
+++ b/gd/cert/run_pts_l2cap.sh
@@ -1,5 +1,5 @@
 #! /bin/bash
 
-# For bluetooth_packets_python3
-export PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64
-python3.8 `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/l2cap/pts/pts.json -tf $ANDROID_BUILD_TOP/system/bt/gd/l2cap/pts/pts_l2cap_testcase -tp $ANDROID_BUILD_TOP/system/bt/gd
+source $ANDROID_BUILD_TOP/system/bt/cert/run \
+  --test_config=$ANDROID_BUILD_TOP/system/bt/gd/cert/pts.json \
+  --test_file=$ANDROID_BUILD_TOP/system/bt/gd/cert/pts_l2cap_testcase
\ No newline at end of file
diff --git a/gd/cert/set_up_acts.sh b/gd/cert/set_up_acts.sh
deleted file mode 100755
index a902efc..0000000
--- a/gd/cert/set_up_acts.sh
+++ /dev/null
@@ -1,109 +0,0 @@
-#! /bin/bash
-#
-# Script to setup environment to execute bluetooth certification stack
-#
-# for more info, see go/acts
-
-## Android build main build setup script relative to top level android source root
-BUILD_SETUP=./build/envsetup.sh
-
-function UsageAndroidTree {
-    cat<<EOF
-Ensure invoked from within the android source tree
-EOF
-}
-
-function UsageSourcedNotExecuted {
-    cat<<EOF
-Ensure script is SOURCED and not executed to persist the build setup
-e.g.
-source $0
-EOF
-}
-
-function UpFind {
-    while [[ $PWD != / ]] ; do
-        rc=$(find "$PWD" -maxdepth 1 "$@")
-        if [ -n "$rc" ]; then
-            echo $(dirname "$rc")
-            return
-        fi
-        cd ..
-    done
-}
-
-function SetUpAndroidBuild {
-    pushd .
-    android_root=$(UpFind -name out -type d)
-    if [[ -z $android_root ]] ; then
-        UsageAndroidTree
-        return
-    fi
-    echo "Found android root $android_root"
-    cd $android_root && . $BUILD_SETUP
-    echo "Sourced build setup rules"
-    cd $android_root && lunch
-    popd
-}
-
-function SetupPython38 {
-    echo "Setting up python3.8"
-    sudo apt-get install python3.8-dev
-}
-
-function CompileBluetoothPacketsPython3 {
-    echo "bluetooth_packets_python3 is not found, compiling"
-    croot
-    make -j bluetooth_packets_python3
-}
-
-if [[ "${BASH_SOURCE[0]}" == "${0}" ]] ; then
-    UsageSourcedNotExecuted
-    exit 1
-fi
-
-if [[ -z "$ANDROID_BUILD_TOP" ]] ; then
-    SetUpAndroidBuild
-fi
-
-## Check python3.8 is installed properly
-## Need Python 3.8 because bluetooth_packets_python3 is compiled against
-## Python 3.8 headers
-dpkg -l python3.8-dev > /dev/null 2>&1
-if [[ $? -ne 0 ]] ; then
-    SetupPython38
-fi
-
-## Check bluetooth_packets_python3 is compiled succssfully
-PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64 python3.8 -c "
-import bluetooth_packets_python3 as bp3
-bp3.BaseStruct
-"
-if [[ $? -ne 0 ]] ; then
-  pushd .
-  CompileBluetoothPacketsPython3
-  popd
-  python3.8 -c "
-import bluetooth_packets_python3 as bp3
-bp3.BaseStruct
-"
-  if [[ $? -ne 0 ]] ; then
-    echo "Setup failed as bluetooth_packets_python3 cannot be found"
-  else
-    echo "Found bluetooth_packets_python3 after compilation"
-  fi
-else
-  echo "Found bluetooth_packets_python3"
-fi
-
-## All is good now so go ahead with the acts setup
-pushd .
-cd $ANDROID_BUILD_TOP/tools/test/connectivity/acts/framework/
-sudo python3.8 setup.py develop
-if [[ $? -eq 0 ]] ; then
-    echo "cert setup complete"
-else
-    echo "cert setup failed"
-fi
-popd
-
diff --git a/gd/cert/set_up_and_run_device_cert.sh b/gd/cert/set_up_and_run_device_cert.sh
new file mode 100755
index 0000000..f768ae8
--- /dev/null
+++ b/gd/cert/set_up_and_run_device_cert.sh
@@ -0,0 +1,158 @@
+#!/bin/bash
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+function menu-adb() {
+    TMP=$(adb devices -l | grep -v "List of device" | awk '{ print $1 }')
+    # TODO(optedoblivion): If the device doesn't have a name (offline), it misnames them
+    NTMP=$(adb devices -l | grep -v "List of device" | awk '{ print $6 }' | cut -d ':' -f 2)
+    SERIALS=($TMP)
+    DEVICES=($NTMP)
+    LEN=${#SERIALS[@]}
+    result=0
+    if [ $LEN -lt 1 ]; then
+        echo "No devices connected!"
+        return 1
+    fi
+
+    if [ "$LEN" == "" ]; then
+        LEN=0
+    fi
+
+    answer=0
+
+    DEVICE_NAME="$1 device"
+
+    if [ $LEN -gt 1 ]; then
+        echo "+-------------------------------------------------+" 1>&2
+        echo "| Choose a ${DEVICE_NAME}:                         " 1>&2
+        echo "+-------------------------------------------------+" 1>&2
+        echo "|                                                 |" 1>&2
+        let fixed_len=$LEN-1
+        for i in `seq 0 $fixed_len`;
+        do
+            serial=${SERIALS[i]}
+            device=${DEVICES[i]}
+            echo "| $i) $serial $device" 1>&2
+            ## TODO[MSB]: Find character count, fill with space and ending box wall
+        done
+        echo "|                                                 |" 1>&2
+        echo "+-------------------------------------------------+" 1>&2
+        echo 1>&2
+        echo -n "Index number: " 1>&2
+        read answer
+    fi
+
+    if [ $answer -ge $LEN ]; then
+        echo
+        echo "Please choose a correct index!" 1>&2
+        echo
+        return 1
+    fi
+
+    SERIAL=${SERIALS[$answer]}
+    echo $SERIAL
+}
+
+function UpFind {
+    while [[ $PWD != / ]] ; do
+        rc=$(find "$PWD" -maxdepth 1 "$@")
+        if [ -n "$rc" ]; then
+            echo $(dirname "$rc")
+            return
+        fi
+        cd ..
+    done
+}
+
+
+function get-android-root() {
+    android_root=$(UpFind -name dalvik -type d)
+    if [[ -z $android_root ]] ; then
+        echo
+        echo "Needs to be ran in the android tree"
+        echo
+        return 1
+    fi
+    echo "${android_root}"
+}
+
+function banner() {
+    echo
+    echo "GD On Device Cert Test"
+    echo
+}
+
+## Main
+banner
+
+DRY_RUN=""
+DO_BUILD=0
+echo "$@"
+if [ $# -gt 0 ]; then
+    for var in "$@"
+    do
+        if [ "$var" == "-h" ]; then
+            echo
+            echo "Usage: $0 [-h|-d]"
+            echo
+            echo "Available Options:"
+            echo "=================="
+            echo " -h | Help(this) Menu"
+            echo " -d | Dry run; just prints commands"
+            echo
+            return 0
+        elif [ "$var" == "-d" ]; then
+            DRY_RUN="echo"
+        elif [ "$var" == "-b" ]; then
+            DO_BUILD=1
+        fi
+    done
+fi
+
+## Verify devices connected and sane
+DUT_SERIAL="$(menu-adb DUT)"
+DUT_ADB="adb -s ${DUT_SERIAL}"
+DUT_NAME="$(adb devices -l | grep -v "List of device" | grep ${DUT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
+
+CERT_SERIAL="$(menu-adb CERT)"
+CERT_ADB="adb -s ${CERT_SERIAL}"
+CERT_NAME="$(adb devices -l | grep -v "List of device" | grep ${CERT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
+
+if [ "${CERT_SERIAL}" == "${DUT_SERIAL}" ]; then
+    echo
+    echo "ERROR: CERT and DUT cannot be the same device, or you only have one device connected!"
+    echo
+    return 1
+fi
+
+## Start builds
+if [ $DO_BUILD == 1 ]; then
+    $DRY_RUN cd $(get-android-root)
+    $DRY_RUN . build/envsetup.sh
+    #DUT
+    $DRY_RUN lunch $DUT_NAME
+    $DRY_RUN cd $(get-android-root)/system/bt/gd
+    $DRY_RUN mma -j `cat /proc/cpuinfo | grep core | wc -l`
+    $DRY_RUN cd $(get-android-root)
+    # CERT
+    $DRY_RUN lunch $CERT_NAME
+    $DRY_RUN cd $(get-android-root)/system/bt/gd
+    $DRY_RUN mma -j `cat /proc/cpuinfo | grep core | wc -l`
+    $DRY_RUN cd $(get-android-root)
+fi
+
+## Set android devices in config
+pushd .
+cd "${DIR}"
+# Reset in case user chooses different item in menu
+git checkout android_devices_config.json
+popd
+$DRY_RUN sed -i "s/\"DUT\"/\"${DUT_SERIAL}\"/g" ${DIR}/android_devices_config.json
+$DRY_RUN sed -i "s/\"CERT\"/\"${CERT_SERIAL}\"/g" ${DIR}/android_devices_config.json
+
+## ACTS
+#$DRY_RUN source $(get-android-root)/system/bt/gd/cert/set_up_acts.sh
+
+## Start test
+$DRY_RUN $(get-android-root)/system/bt/gd/cert/run
diff --git a/gd/cert/set_up_virtualenv.sh b/gd/cert/set_up_virtualenv.sh
new file mode 100644
index 0000000..0c07691
--- /dev/null
+++ b/gd/cert/set_up_virtualenv.sh
@@ -0,0 +1,145 @@
+#! /bin/bash
+#
+# Script to setup virtual environment to run GD cert tests and devlop using IDE
+#
+# Usage
+#  1. cd system/bt/gd
+#  2. source cert/set_up_virtualenv.sh
+#  4. [run tests, do development, hack] using vevn/bin/python
+#
+# Note: Just use the virtualized Python binary, no need to activate
+
+## Android build main build setup script relative to top level android source root
+BUILD_SETUP=./build/envsetup.sh
+
+function UsageAndroidTree {
+    cat<<EOF
+Ensure invoked from within the android source tree
+EOF
+}
+
+function UsageSourcedNotExecuted {
+    cat<<EOF
+Ensure script is SOURCED and not executed to persist the build setup
+e.g.
+source $0
+EOF
+}
+
+function UpFind {
+    while [[ $PWD != / ]] ; do
+        rc=$(find "$PWD" -maxdepth 1 "$@")
+        if [ -n "$rc" ]; then
+            echo $(dirname "$rc")
+            return
+        fi
+        cd ..
+    done
+}
+
+function SetUpAndroidBuild {
+    pushd .
+    android_root=$(UpFind -name out -type d)
+    if [[ -z $android_root ]] ; then
+        UsageAndroidTree
+        return
+    fi
+    echo "Found android root $android_root"
+    cd $android_root && . $BUILD_SETUP
+    echo "Sourced build setup rules"
+    cd $android_root && lunch
+    popd
+}
+
+function SetupPython38 {
+    echo "Setting up python3.8"
+    sudo apt-get install python3.8-dev
+}
+
+function SetupPip3 {
+    echo "Setting up pip3"
+    sudo apt-get install python3-pip
+}
+
+# Deactivate existing virtual environment, if any, ignore errors
+deactivate > /dev/null 2>&1
+
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]] ; then
+    UsageSourcedNotExecuted
+    return 1
+fi
+
+## Check python3.8 is installed properly
+## Need Python 3.8 because bluetooth_packets_python3 is compiled against
+## Python 3.8 headers
+dpkg -l python3.8-dev > /dev/null 2>&1
+if [[ $? -ne 0 ]] ; then
+    SetupPython38
+fi
+
+## Check pip3 is installed properly
+## Need pip3 for Python 3 support
+dpkg -l python3-pip > /dev/null 2>&1
+if [[ $? -ne 0 ]] ; then
+    SetupPip3
+fi
+
+# Install and upgrade virtualenv to latest version
+pip3 install --user --upgrade virtualenv > /dev/null 2>&1
+if [[ $? -ne 0 ]] ; then
+    echo "Error install and upgrade virtualenv"
+    return 1
+fi
+
+# Set-up Android environment variables
+if [[ -z "$ANDROID_BUILD_TOP" ]] ; then
+    SetUpAndroidBuild
+fi
+
+## Compile and unzip test artifacts
+echo "Compiling bluetooth_stack_with_facade ..."
+$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="$(pwd)" dist bluetooth_stack_with_facade
+if [[ $? -ne 0 ]] ; then
+    echo "Failed to compile bluetooth_stack_with_facade"
+    return 1
+fi
+if [[ ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_tests.zip" ]]; then
+    echo "Cannot find bluetooth_cert_tests.zip after compilation"
+    return 1
+fi
+
+CERT_TEST_VENV=$ANDROID_BUILD_TOP/out/dist/bluetooth_venv
+
+rm -rf $CERT_TEST_VENV
+
+python3.8 -m virtualenv --python `which python3.8` $CERT_TEST_VENV
+if [[ $? -ne 0 ]] ; then
+    echo "Error setting up virtualenv"
+    return 1
+fi
+
+unzip -o -q $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_tests.zip -d $CERT_TEST_VENV/acts
+if [[ $? -ne 0 ]] ; then
+    echo "Error unzipping bluetooth_cert_tests.zip"
+    return 1
+fi
+
+$CERT_TEST_VENV/bin/python $CERT_TEST_VENV/acts/setup.py install
+if [[ $? -ne 0 ]] ; then
+    echo "Error installing GD libraries"
+    return 1
+fi
+
+$CERT_TEST_VENV/bin/python -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
+if [[ $? -ne 0 ]] ; then
+  echo "Setup failed as bluetooth_packets_python3 cannot be imported"
+  return 1
+fi
+
+echo ""
+echo "Please mark GD root directory as \"Project Sources and Headers\" in IDE"
+echo "If still seeing errors, invalidate cached and restart"
+echo "virtualenv setup complete"
diff --git a/gd/cert/truth.py b/gd/cert/truth.py
new file mode 100644
index 0000000..eba6be3
--- /dev/null
+++ b/gd/cert/truth.py
@@ -0,0 +1,150 @@
+#!/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 datetime import timedelta
+
+from mobly.asserts import assert_true
+from mobly.asserts import assert_false
+from mobly import signals
+
+from cert.event_stream import IEventStream
+from cert.event_stream import NOT_FOR_YOU_assert_event_occurs
+from cert.event_stream import NOT_FOR_YOU_assert_all_events_occur
+from cert.event_stream import NOT_FOR_YOU_assert_none_matching
+from cert.event_stream import NOT_FOR_YOU_assert_none
+
+
+class ObjectSubject(object):
+
+    def __init__(self, value):
+        self._value = value
+
+    def isEqualTo(self, other):
+        if self._value != other:
+            raise signals.TestFailure("Expected \"%s\" to be equal to \"%s\"" % (self._value, other), extras=None)
+
+    def isNotEqualTo(self, other):
+        if self._value == other:
+            raise signals.TestFailure("Expected \"%s\" to not be equal to \"%s\"" % (self._value, other), extras=None)
+
+    def isNone(self):
+        if self._value is not None:
+            raise signals.TestFailure("Expected \"%s\" to be None" % self._value, extras=None)
+
+    def isNotNone(self):
+        if self._value is None:
+            raise signals.TestFailure("Expected \"%s\" to not be None" % self._value, extras=None)
+
+
+DEFAULT_TIMEOUT = timedelta(seconds=3)
+
+
+class EventStreamSubject(ObjectSubject):
+
+    def __init__(self, value):
+        super().__init__(value)
+
+    def emits(self, *match_fns, at_least_times=1, timeout=DEFAULT_TIMEOUT):
+        if len(match_fns) == 0:
+            raise signals.TestFailure("Must specify a match function")
+        elif len(match_fns) == 1:
+            NOT_FOR_YOU_assert_event_occurs(self._value, match_fns[0], at_least_times=at_least_times, timeout=timeout)
+            return EventStreamContinuationSubject(self._value)
+        else:
+            return MultiMatchStreamSubject(self._value, match_fns, timeout)
+
+    def emitsNone(self, *match_fns, timeout=DEFAULT_TIMEOUT):
+        if len(match_fns) == 0:
+            NOT_FOR_YOU_assert_none(self._value, timeout=timeout)
+            return EventStreamContinuationSubject(self._value)
+        elif len(match_fns) == 1:
+            NOT_FOR_YOU_assert_none_matching(self._value, match_fns[0], timeout=timeout)
+            return EventStreamContinuationSubject(self._value)
+        else:
+            raise signals.TestFailure("Cannot specify multiple match functions")
+
+
+class MultiMatchStreamSubject(object):
+
+    def __init__(self, stream, match_fns, timeout):
+        self._stream = stream
+        self._match_fns = match_fns
+        self._timeout = timeout
+
+    def inAnyOrder(self):
+        NOT_FOR_YOU_assert_all_events_occur(self._stream, self._match_fns, order_matters=False, timeout=self._timeout)
+        return EventStreamContinuationSubject(self._stream)
+
+    def inOrder(self):
+        NOT_FOR_YOU_assert_all_events_occur(self._stream, self._match_fns, order_matters=True, timeout=self._timeout)
+        return EventStreamContinuationSubject(self._stream)
+
+
+class EventStreamContinuationSubject(ObjectSubject):
+
+    def __init__(self, value):
+        super().__init__(value)
+
+    def then(self, *match_fns, at_least_times=1, timeout=DEFAULT_TIMEOUT):
+        if len(match_fns) == 0:
+            raise signals.TestFailure("Must specify a match function")
+        elif len(match_fns) == 1:
+            NOT_FOR_YOU_assert_event_occurs(self._value, match_fns[0], at_least_times=at_least_times, timeout=timeout)
+            return EventStreamContinuationSubject(self._value)
+        else:
+            return MultiMatchStreamSubject(self._value, match_fns, timeout)
+
+    def thenNone(self, *match_fns, timeout=DEFAULT_TIMEOUT):
+        if len(match_fns) == 0:
+            NOT_FOR_YOU_assert_none(self._value, timeout=timeout)
+            return EventStreamContinuationSubject(self._value)
+        elif len(match_fns) == 1:
+            NOT_FOR_YOU_assert_none_matching(self._value, match_fns[0], timeout=timeout)
+            return EventStreamContinuationSubject(self._value)
+        else:
+            raise signals.TestFailure("Cannot specify multiple match functions")
+
+
+class BooleanSubject(ObjectSubject):
+
+    def __init__(self, value):
+        super().__init__(value)
+
+    def isTrue(self):
+        assert_true(self._value, "")
+
+    def isFalse(self):
+        assert_false(self._value, "")
+
+
+class TimeDeltaSubject(ObjectSubject):
+
+    def __init__(self, value):
+        super().__init__(value)
+
+    def isWithin(self, time_bound):
+        assert_true(self._value < time_bound, "")
+
+
+def assertThat(subject):
+    if type(subject) is bool:
+        return BooleanSubject(subject)
+    elif isinstance(subject, IEventStream):
+        return EventStreamSubject(subject)
+    elif isinstance(subject, timedelta):
+        return TimeDeltaSubject(subject)
+    else:
+        return ObjectSubject(subject)
diff --git a/gd/common/Android.bp b/gd/common/Android.bp
index c923427..2f33b72 100644
--- a/gd/common/Android.bp
+++ b/gd/common/Android.bp
@@ -2,6 +2,8 @@
     name: "BluetoothCommonSources",
     srcs: [
         "link_key.cc",
+        "init_flags.cc",
+        "strings.cc",
     ],
 }
 
@@ -12,5 +14,9 @@
         "bidi_queue_unittest.cc",
         "observer_registry_test.cc",
         "link_key_unittest.cc",
+        "init_flags_test.cc",
+        "list_map_test.cc",
+        "lru_cache_test.cc",
+        "strings_test.cc",
     ],
 }
diff --git a/gd/common/bidi_queue.h b/gd/common/bidi_queue.h
index c108588..0fdf085 100644
--- a/gd/common/bidi_queue.h
+++ b/gd/common/bidi_queue.h
@@ -25,16 +25,13 @@
 namespace common {
 
 template <typename TENQUEUE, typename TDEQUEUE>
-class BidiQueueEnd
-    : public ::bluetooth::os::IQueueEnqueue<TENQUEUE>,
-      public ::bluetooth::os::IQueueDequeue<TDEQUEUE> {
+class BidiQueueEnd : public ::bluetooth::os::IQueueEnqueue<TENQUEUE>, public ::bluetooth::os::IQueueDequeue<TDEQUEUE> {
  public:
   using EnqueueCallback = Callback<std::unique_ptr<TENQUEUE>()>;
   using DequeueCallback = Callback<void()>;
 
   BidiQueueEnd(::bluetooth::os::IQueueEnqueue<TENQUEUE>* tx, ::bluetooth::os::IQueueDequeue<TDEQUEUE>* rx)
-      : tx_(tx), rx_(rx) {
-  }
+      : tx_(tx), rx_(rx) {}
 
   void RegisterEnqueue(::bluetooth::os::Handler* handler, EnqueueCallback callback) override {
     tx_->RegisterEnqueue(handler, callback);
@@ -68,8 +65,7 @@
       : up_queue_(capacity),
         down_queue_(capacity),
         up_end_(&down_queue_, &up_queue_),
-        down_end_(&up_queue_, &down_queue_) {
-  }
+        down_end_(&up_queue_, &down_queue_) {}
 
   BidiQueueEnd<TDOWN, TUP>* GetUpEnd() {
     return &up_end_;
diff --git a/gd/common/bidi_queue_unittest.cc b/gd/common/bidi_queue_unittest.cc
index 7a5503c..1559275 100644
--- a/gd/common/bidi_queue_unittest.cc
+++ b/gd/common/bidi_queue_unittest.cc
@@ -23,8 +23,8 @@
 #include "os/handler.h"
 #include "os/thread.h"
 
-using ::bluetooth::os::Thread;
 using ::bluetooth::os::Handler;
+using ::bluetooth::os::Thread;
 
 namespace bluetooth {
 namespace common {
@@ -52,17 +52,14 @@
   Handler* down_handler_;
 };
 
-class A {
-};
+class A {};
 
-class B {
-};
+class B {};
 
 template <typename TA, typename TB>
 class TestBidiQueueEnd {
  public:
-  explicit TestBidiQueueEnd(BidiQueueEnd<TA, TB>* end, Handler* handler)
-      : handler_(handler), end_(end) {}
+  explicit TestBidiQueueEnd(BidiQueueEnd<TA, TB>* end, Handler* handler) : handler_(handler), end_(end) {}
 
   ~TestBidiQueueEnd() {
     handler_->Clear();
@@ -70,8 +67,11 @@
 
   std::promise<void>* Send(TA* value) {
     std::promise<void>* promise = new std::promise<void>();
-    handler_->Post(BindOnce(&TestBidiQueueEnd<TA, TB>::handle_send, common::Unretained(this), common::Unretained(value),
-                            common::Unretained(promise)));
+    handler_->Post(BindOnce(
+        &TestBidiQueueEnd<TA, TB>::handle_send,
+        common::Unretained(this),
+        common::Unretained(value),
+        common::Unretained(promise)));
     return promise;
   }
 
@@ -84,8 +84,13 @@
   }
 
   void handle_send(TA* value, std::promise<void>* promise) {
-    end_->RegisterEnqueue(handler_, Bind(&TestBidiQueueEnd<TA, TB>::handle_register_enqueue, common::Unretained(this),
-                                         common::Unretained(value), common::Unretained(promise)));
+    end_->RegisterEnqueue(
+        handler_,
+        Bind(
+            &TestBidiQueueEnd<TA, TB>::handle_register_enqueue,
+            common::Unretained(this),
+            common::Unretained(value),
+            common::Unretained(promise)));
   }
 
   std::unique_ptr<TA> handle_register_enqueue(TA* value, std::promise<void>* promise) {
@@ -95,8 +100,10 @@
   }
 
   void handle_receive(std::promise<TB*>* promise) {
-    end_->RegisterDequeue(handler_, Bind(&TestBidiQueueEnd<TA, TB>::handle_register_dequeue, common::Unretained(this),
-                                         common::Unretained(promise)));
+    end_->RegisterDequeue(
+        handler_,
+        Bind(
+            &TestBidiQueueEnd<TA, TB>::handle_register_dequeue, common::Unretained(this), common::Unretained(promise)));
   }
 
   void handle_register_dequeue(std::promise<TB*>* promise) {
@@ -132,5 +139,5 @@
 }
 
 }  // namespace
-}  // namespace os
+}  // namespace common
 }  // namespace bluetooth
diff --git a/gd/common/bind.h b/gd/common/bind.h
index 1adf16c..e81edb7 100644
--- a/gd/common/bind.h
+++ b/gd/common/bind.h
@@ -25,10 +25,16 @@
 using base::BindOnce;
 using base::ConstRef;
 using base::IgnoreResult;
+using base::MakeUnboundRunType;
 using base::Owned;
 using base::Passed;
 using base::RetainedRef;
 using base::Unretained;
 
+template <typename T, typename Functor, typename... Args>
+inline base::Callback<MakeUnboundRunType<Functor, T, Args...>> BindOn(T* obj, Functor&& functor, Args&&... args) {
+  return common::Bind(std::forward<Functor>(functor), common::Unretained(obj), std::forward<Args>(args)...);
+}
+
 }  // namespace common
 }  // namespace bluetooth
diff --git a/gd/common/blocking_queue_unittest.cc b/gd/common/blocking_queue_unittest.cc
index 57a6245..b6c9632 100644
--- a/gd/common/blocking_queue_unittest.cc
+++ b/gd/common/blocking_queue_unittest.cc
@@ -16,10 +16,10 @@
 
 #include "common/blocking_queue.h"
 
-#include <thread>
-
 #include <gtest/gtest.h>
 
+#include <thread>
+
 namespace bluetooth {
 namespace common {
 namespace {
diff --git a/gd/common/callback_list.h b/gd/common/callback_list.h
index 857b0c7..a3ad6b1 100644
--- a/gd/common/callback_list.h
+++ b/gd/common/callback_list.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <utility>
+
 #include "base/callback_list.h"
 #include "os/handler.h"
 
diff --git a/gd/common/contextual_callback.h b/gd/common/contextual_callback.h
new file mode 100644
index 0000000..5393529
--- /dev/null
+++ b/gd/common/contextual_callback.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/bind.h"
+#include "common/callback.h"
+
+namespace bluetooth {
+namespace common {
+
+class IPostableContext {
+ public:
+  virtual ~IPostableContext(){};
+  virtual void Post(OnceClosure closure) = 0;
+};
+
+template <typename R, typename... Args>
+class ContextualOnceCallback;
+
+// A callback bound to an execution context that can be invoked only once.
+template <typename R, typename... Args>
+class ContextualOnceCallback<R(Args...)> {
+ public:
+  ContextualOnceCallback(common::OnceCallback<R(Args...)>&& callback, IPostableContext* context)
+      : callback_(std::move(callback)), context_(context) {}
+
+  constexpr ContextualOnceCallback() = default;
+
+  DISALLOW_COPY_AND_ASSIGN(ContextualOnceCallback);
+
+  ContextualOnceCallback(ContextualOnceCallback&&) noexcept = default;
+  ContextualOnceCallback& operator=(ContextualOnceCallback&&) noexcept = default;
+
+  void Invoke(Args... args) {
+    context_->Post(common::BindOnce(std::move(callback_), std::forward<Args>(args)...));
+  }
+
+  void InvokeIfNotEmpty(Args... args) {
+    if (context_ != nullptr) {
+      context_->Post(common::BindOnce(std::move(callback_), std::forward<Args>(args)...));
+    }
+  }
+
+  bool IsEmpty() {
+    return context_ == nullptr;
+  }
+
+ private:
+  common::OnceCallback<R(Args...)> callback_;
+  IPostableContext* context_;
+};
+
+template <typename R, typename... Args>
+class ContextualCallback;
+
+// A callback bound to an execution context that can be invoked multiple times.
+template <typename R, typename... Args>
+class ContextualCallback<R(Args...)> {
+ public:
+  ContextualCallback(common::Callback<R(Args...)>&& callback, IPostableContext* context)
+      : callback_(std::move(callback)), context_(context) {}
+
+  constexpr ContextualCallback() = default;
+
+  ContextualCallback(const ContextualCallback&) = default;
+  ContextualCallback& operator=(const ContextualCallback&) = default;
+  ContextualCallback(ContextualCallback&&) noexcept = default;
+  ContextualCallback& operator=(ContextualCallback&&) noexcept = default;
+
+  void Invoke(Args... args) {
+    context_->Post(common::BindOnce(callback_, std::forward<Args>(args)...));
+  }
+
+  void InvokeIfNotEmpty(Args... args) {
+    if (context_ != nullptr) {
+      context_->Post(common::BindOnce(callback_, std::forward<Args>(args)...));
+    }
+  }
+
+  bool IsEmpty() {
+    return context_ == nullptr;
+  }
+
+ private:
+  common::Callback<R(Args...)> callback_;
+  IPostableContext* context_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/init_flags.cc b/gd/common/init_flags.cc
new file mode 100644
index 0000000..ea9b95c
--- /dev/null
+++ b/gd/common/init_flags.cc
@@ -0,0 +1,44 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "init_flags.h"
+
+#include <string>
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace common {
+
+const std::string kGdCoreFlag = "INIT_gd_core";
+bool InitFlags::gd_core_enabled = false;
+
+void InitFlags::Load(const char** flags) {
+  gd_core_enabled = false;
+  while (flags != nullptr && *flags != nullptr) {
+    if (kGdCoreFlag == *flags) {
+      gd_core_enabled = true;
+    }
+    flags++;
+  }
+
+  LOG_INFO("Flags loaded: gd_core_enabled: %s", gd_core_enabled ? "true" : "false");
+}
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/l2cap/security_policy.h b/gd/common/init_flags.h
similarity index 74%
copy from gd/l2cap/security_policy.h
copy to gd/common/init_flags.h
index 5a06401..e19c21d 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/common/init_flags.h
@@ -13,12 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
 namespace bluetooth {
-namespace l2cap {
+namespace common {
 
-class SecurityPolicy {};
+class InitFlags final {
+ public:
+  static void Load(const char** flags);
 
-}  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+  static bool GdCoreEnabled() {
+    return gd_core_enabled;
+  }
+
+ private:
+  static bool gd_core_enabled;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/init_flags_test.cc b/gd/common/init_flags_test.cc
new file mode 100644
index 0000000..adf412e
--- /dev/null
+++ b/gd/common/init_flags_test.cc
@@ -0,0 +1,46 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "common/init_flags.h"
+
+#include <gtest/gtest.h>
+
+using bluetooth::common::InitFlags;
+
+TEST(InitFlagsTest, test_load_nullptr) {
+  InitFlags::Load(nullptr);
+  ASSERT_EQ(false, InitFlags::GdCoreEnabled());
+}
+
+TEST(InitFlagsTest, test_load_empty) {
+  const char* input[] = {nullptr};
+  InitFlags::Load(input);
+  ASSERT_EQ(false, InitFlags::GdCoreEnabled());
+}
+
+TEST(InitFlagsTest, test_load_garbage) {
+  const char* input[] = {"some random non-existent flag", nullptr};
+  InitFlags::Load(input);
+  ASSERT_EQ(false, InitFlags::GdCoreEnabled());
+}
+
+TEST(InitFlagsTest, test_load_good_case) {
+  const char* input[] = {"INIT_gd_core", nullptr};
+  InitFlags::Load(input);
+  ASSERT_EQ(true, InitFlags::GdCoreEnabled());
+}
diff --git a/gd/common/link_key_unittest.cc b/gd/common/link_key_unittest.cc
index 6529564..65d2a13 100644
--- a/gd/common/link_key_unittest.cc
+++ b/gd/common/link_key_unittest.cc
@@ -17,7 +17,9 @@
  ******************************************************************************/
 
 #include "common/link_key.h"
+
 #include <gtest/gtest.h>
+
 #include "os/log.h"
 
 using bluetooth::common::LinkKey;
@@ -25,8 +27,8 @@
 static const char* test_link_key = "4c68384139f574d836bcf34e9dfb01bf\0";
 
 TEST(LinkKeyUnittest, test_constructor_array) {
-  uint8_t data[LinkKey::kLength] = {0x4c, 0x87, 0x49, 0xe1, 0x2e, 0x55, 0x0f, 0x7f,
-                                    0x60, 0x8b, 0x4f, 0x96, 0xd7, 0xc5, 0xbc, 0x2a};
+  uint8_t data[LinkKey::kLength] = {
+      0x4c, 0x87, 0x49, 0xe1, 0x2e, 0x55, 0x0f, 0x7f, 0x60, 0x8b, 0x4f, 0x96, 0xd7, 0xc5, 0xbc, 0x2a};
 
   LinkKey link_key(data);
 
diff --git a/gd/common/list_map.h b/gd/common/list_map.h
new file mode 100644
index 0000000..babf869
--- /dev/null
+++ b/gd/common/list_map.h
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <iterator>
+#include <list>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <type_traits>
+#include <unordered_map>
+
+namespace bluetooth {
+namespace common {
+
+// A map that maintains order of its element as a list. An element that is put earlier will appear before an element
+// that is put later when iterating through this map's entries. Keys must be unique.
+//
+// Performance:
+//   - Key look-up and modification is O(1)
+//   - Value operated by replacement, no in-place modification
+//   - Memory consumption is:
+//     O(2*capacity*sizeof(K) + capacity*(sizeof(nullptr)+sizeof(V)))
+//   - NOT THREAD SAFE
+//
+// Template:
+//   - Key key
+//   - T value
+template <typename Key, typename T>
+class ListMap {
+ public:
+  using value_type = std::pair<const Key, T>;
+  // different from c++17 node_type on purpose as we want node to be copyable
+  using node_type = std::pair<Key, T>;
+  using iterator = typename std::list<value_type>::iterator;
+  using const_iterator = typename std::list<value_type>::const_iterator;
+
+  // Constructor of the list map
+  ListMap() = default;
+
+  // for move
+  ListMap(ListMap&& other) noexcept = default;
+  ListMap& operator=(ListMap&& other) noexcept = default;
+
+  // copy-constructor
+  // iterators in key_map_ cannot be copied directly
+  ListMap(const ListMap& other) : node_list_(other.node_list_) {
+    for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) {
+      key_map_.emplace(iter->first, iter);
+    }
+  }
+
+  // copy-assignment
+  // iterators in key_map_ cannot be copied directly
+  ListMap& operator=(const ListMap& other) {
+    if (&other == this) {
+      return *this;
+    }
+    node_list_ = other.node_list_;
+    key_map_.clear();
+    for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) {
+      key_map_.emplace(iter->first, iter);
+    }
+    return *this;
+  }
+
+  // comparison operators
+  bool operator==(const ListMap& rhs) const {
+    return node_list_ == rhs.node_list_;
+  }
+  bool operator!=(const ListMap& rhs) const {
+    return !(*this == rhs);
+  }
+
+  ~ListMap() {
+    clear();
+  }
+
+  // Clear the list map
+  void clear() {
+    key_map_.clear();
+    node_list_.clear();
+  }
+
+  // const version of find()
+  const_iterator find(const Key& key) const {
+    return const_cast<ListMap*>(this)->find(key);
+  }
+
+  // Get the value of a key. Return iterator to the item if found, end() if not found
+  iterator find(const Key& key) {
+    auto map_iterator = key_map_.find(key);
+    if (map_iterator == key_map_.end()) {
+      return end();
+    }
+    return map_iterator->second;
+  }
+
+  // Check if key exist in the map. Return true if key exist in map, false if not.
+  bool contains(const Key& key) const {
+    return find(key) != end();
+  }
+
+  // Try emplace an element before a specific position |pos| of the list map. If the |key| already exists, does nothing.
+  // Moved arguments won't be moved when key already exists. Return <iterator, true> when key does not exist, <iterator,
+  // false> when key exist and iterator is the position where it was placed.
+  template <class... Args>
+  std::pair<iterator, bool> try_emplace(const_iterator pos, const Key& key, Args&&... args) {
+    auto map_iterator = key_map_.find(key);
+    if (map_iterator != key_map_.end()) {
+      return std::make_pair(end(), false);
+    }
+    auto list_iterator = node_list_.emplace(pos, key, std::forward<Args>(args)...);
+    key_map_.emplace(key, list_iterator);
+    return std::make_pair(list_iterator, true);
+  }
+
+  // Try emplace an element before the end of the list map. If the key already exists, does nothing. Moved arguments
+  // won't be moved when key already exists return <iterator, true> when key does not exist, <iterator, false> when key
+  // exist and iterator is the position where it was placed
+  template <class... Args>
+  std::pair<iterator, bool> try_emplace_back(const Key& key, Args&&... args) {
+    return try_emplace(end(), key, std::forward<Args>(args)...);
+  }
+
+  // Put a key-value pair to the map before position. If key already exist, |pos| will be ignored and existing value
+  // will be replaced
+  void insert_or_assign(const_iterator pos, const Key& key, T value) {
+    auto map_iterator = key_map_.find(key);
+    if (map_iterator != key_map_.end()) {
+      map_iterator->second->second = std::move(value);
+      return;
+    }
+    auto list_iterator = node_list_.emplace(pos, key, std::move(value));
+    key_map_.emplace(key, list_iterator);
+  }
+
+  // Put a key-value pair to the tail of the map or replace the current value without moving the key if key exists
+  void insert_or_assign(const Key& key, T value) {
+    insert_or_assign(end(), key, std::move(value));
+  }
+
+  // STL splice, same as std::list::splice
+  // - pos: element before which the content will be inserted
+  // - other: another container to transfer the content from
+  // - it: the element to transfer from other to *this
+  void splice(const_iterator pos, ListMap<Key, T>& other, const_iterator it) {
+    if (&other != this) {
+      auto map_node = other.key_map_.extract(it->first);
+      key_map_.insert(std::move(map_node));
+    }
+    node_list_.splice(pos, other.node_list_, it);
+  }
+
+  // Remove a key from the list map and return removed value if key exits, std::nullopt if not. The return value will be
+  // evaluated to true in a boolean context if a value is contained by std::optional, false otherwise.
+  std::optional<node_type> extract(const Key& key) {
+    auto map_iterator = key_map_.find(key);
+    if (map_iterator == key_map_.end()) {
+      return std::nullopt;
+    }
+    std::optional<node_type> removed_node(std::move(*map_iterator->second));
+    node_list_.erase(map_iterator->second);
+    key_map_.erase(map_iterator);
+    return removed_node;
+  }
+
+  // Remove an iterator pointed item from the list map and return the iterator immediately after the erased item
+  iterator erase(const_iterator iter) {
+    key_map_.erase(iter->first);
+    return node_list_.erase(iter);
+  }
+
+  // Return size of the list map
+  inline size_t size() const {
+    return node_list_.size();
+  }
+
+  // Return iterator interface for begin
+  inline iterator begin() {
+    return node_list_.begin();
+  }
+
+  // Iterator interface for begin, const
+  inline const_iterator begin() const {
+    return node_list_.begin();
+  }
+
+  // Iterator interface for end
+  inline iterator end() {
+    return node_list_.end();
+  }
+
+  // Iterator interface for end, const
+  inline const_iterator end() const {
+    return node_list_.end();
+  }
+
+ private:
+  std::list<value_type> node_list_;
+  std::unordered_map<Key, iterator> key_map_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/common/list_map_test.cc b/gd/common/list_map_test.cc
new file mode 100644
index 0000000..e682eea
--- /dev/null
+++ b/gd/common/list_map_test.cc
@@ -0,0 +1,363 @@
+/*
+ * 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.
+ */
+
+#include <chrono>
+#include <memory>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "common/list_map.h"
+
+namespace testing {
+
+using bluetooth::common::ListMap;
+
+TEST(ListMapTest, empty_test) {
+  ListMap<int, int> list_map;
+  EXPECT_EQ(list_map.size(), 0);
+  EXPECT_EQ(list_map.find(42), list_map.end());
+  list_map.clear();  // should not crash
+  EXPECT_EQ(list_map.find(42), list_map.end());
+  EXPECT_FALSE(list_map.contains(42));
+  EXPECT_FALSE(list_map.extract(42));
+}
+
+TEST(ListMapTest, comparison_test) {
+  ListMap<int, int> list_map_1;
+  list_map_1.insert_or_assign(1, 10);
+  list_map_1.insert_or_assign(2, 20);
+  ListMap<int, int> list_map_2;
+  list_map_2.insert_or_assign(1, 10);
+  list_map_2.insert_or_assign(2, 20);
+  EXPECT_EQ(list_map_1, list_map_2);
+  // List map with different value should be different
+  list_map_2.insert_or_assign(1, 11);
+  EXPECT_NE(list_map_1, list_map_2);
+  // List maps with different order should not be equal
+  ListMap<int, int> list_map_3;
+  list_map_3.insert_or_assign(2, 20);
+  list_map_3.insert_or_assign(1, 10);
+  EXPECT_NE(list_map_1, list_map_3);
+  // Empty list map should not be equal to non-empty ones
+  ListMap<int, int> list_map_4;
+  EXPECT_NE(list_map_1, list_map_4);
+  // Empty list maps should be equal
+  ListMap<int, int> list_map_5;
+  EXPECT_EQ(list_map_4, list_map_5);
+}
+
+TEST(ListMapTest, copy_test) {
+  ListMap<int, std::shared_ptr<int>> list_map;
+  list_map.insert_or_assign(1, std::make_shared<int>(100));
+  auto iter = list_map.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  ListMap<int, std::shared_ptr<int>> new_list_map = list_map;
+  iter = new_list_map.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  *iter->second = 300;
+  iter = new_list_map.find(1);
+  EXPECT_EQ(*iter->second, 300);
+  // Since copy is used, shared_ptr should increase count
+  EXPECT_EQ(iter->second.use_count(), 2);
+}
+
+TEST(ListMapTest, move_test) {
+  ListMap<int, std::shared_ptr<int>> list_map;
+  list_map.insert_or_assign(1, std::make_shared<int>(100));
+  auto iter = list_map.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  ListMap<int, std::shared_ptr<int>> new_list_map = std::move(list_map);
+  iter = new_list_map.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  *iter->second = 300;
+  iter = new_list_map.find(1);
+  EXPECT_EQ(*iter->second, 300);
+  // Since move is used, shared_ptr should not increase count
+  EXPECT_EQ(iter->second.use_count(), 1);
+}
+
+TEST(ListMapTest, move_insert_unique_ptr_test) {
+  ListMap<int, std::unique_ptr<int>> list_map;
+  list_map.insert_or_assign(1, std::make_unique<int>(100));
+  auto iter = list_map.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  list_map.insert_or_assign(1, std::make_unique<int>(400));
+  iter = list_map.find(1);
+  EXPECT_EQ(*iter->second, 400);
+}
+
+TEST(ListMapTest, move_insert_list_map_test) {
+  ListMap<int, ListMap<int, int>> list_map;
+  ListMap<int, int> m1;
+  m1.insert_or_assign(1, 100);
+  list_map.insert_or_assign(1, std::move(m1));
+  auto iter = list_map.find(1);
+  EXPECT_THAT(iter->second, ElementsAre(Pair(1, 100)));
+  ListMap<int, int> m2;
+  m2.insert_or_assign(2, 200);
+  list_map.insert_or_assign(1, std::move(m2));
+  iter = list_map.find(1);
+  EXPECT_THAT(iter->second, ElementsAre(Pair(2, 200)));
+}
+
+TEST(ListMapTest, erase_one_item_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  list_map.insert_or_assign(3, 30);
+  auto iter = list_map.find(2);
+  iter = list_map.erase(iter);
+  EXPECT_EQ(iter->first, 3);
+  EXPECT_EQ(iter->second, 30);
+}
+
+TEST(ListMapTest, erase_in_for_loop_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  list_map.insert_or_assign(3, 30);
+  for (auto iter = list_map.begin(); iter != list_map.end();) {
+    if (iter->first == 2) {
+      iter = list_map.erase(iter);
+    } else {
+      ++iter;
+    }
+  }
+  EXPECT_THAT(list_map, ElementsAre(Pair(1, 10), Pair(3, 30)));
+}
+
+TEST(ListMapTest, splice_different_list_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  list_map.insert_or_assign(3, 30);
+  ListMap<int, int> list_map_2;
+  list_map_2.insert_or_assign(4, 40);
+  list_map_2.insert_or_assign(5, 50);
+  list_map.splice(list_map.find(2), list_map_2, list_map_2.find(4));
+  EXPECT_EQ(list_map_2.find(4), list_map_2.end());
+  auto iter = list_map.find(4);
+  EXPECT_NE(iter, list_map.end());
+  EXPECT_EQ(iter->second, 40);
+  EXPECT_THAT(list_map, ElementsAre(Pair(1, 10), Pair(4, 40), Pair(2, 20), Pair(3, 30)));
+}
+
+TEST(ListMapTest, splice_same_list_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  list_map.insert_or_assign(3, 30);
+  list_map.splice(list_map.find(2), list_map, list_map.find(3));
+  EXPECT_THAT(list_map, ElementsAre(Pair(1, 10), Pair(3, 30), Pair(2, 20)));
+  list_map.extract(2);
+  list_map.insert_or_assign(list_map.begin(), 4, 40);
+  EXPECT_THAT(list_map, ElementsAre(Pair(4, 40), Pair(1, 10), Pair(3, 30)));
+  auto iter = list_map.find(4);
+  EXPECT_EQ(iter->second, 40);
+  list_map.splice(list_map.begin(), list_map, list_map.find(4));
+  list_map.splice(list_map.begin(), list_map, list_map.find(3));
+  list_map.splice(list_map.begin(), list_map, list_map.find(1));
+  EXPECT_THAT(list_map, ElementsAre(Pair(1, 10), Pair(3, 30), Pair(4, 40)));
+  iter = list_map.find(4);
+  EXPECT_EQ(iter->second, 40);
+  iter = list_map.find(3);
+  EXPECT_EQ(iter->second, 30);
+}
+
+TEST(ListMapTest, put_get_and_contains_key_test) {
+  ListMap<int, int> list_map;
+  EXPECT_EQ(list_map.size(), 0);
+  EXPECT_EQ(list_map.find(42), list_map.end());
+  EXPECT_FALSE(list_map.contains(42));
+  list_map.insert_or_assign(56, 200);
+  EXPECT_EQ(list_map.find(42), list_map.end());
+  EXPECT_FALSE(list_map.contains(42));
+  auto iter = list_map.find(56);
+  EXPECT_NE(iter, list_map.end());
+  EXPECT_TRUE(list_map.contains(56));
+  EXPECT_EQ(iter->second, 200);
+  EXPECT_TRUE(list_map.extract(56));
+  EXPECT_FALSE(list_map.contains(56));
+}
+
+TEST(ListMapTest, try_emplace_at_position_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  auto iter = list_map.find(2);
+  EXPECT_EQ(iter->second, 20);
+  auto result = list_map.try_emplace(iter, 42, 420);
+  EXPECT_TRUE(result.second);
+  iter = list_map.find(42);
+  EXPECT_EQ(iter->second, 420);
+  EXPECT_EQ(iter, result.first);
+  ASSERT_THAT(list_map, ElementsAre(Pair(1, 10), Pair(42, 420), Pair(2, 20)));
+  EXPECT_FALSE(list_map.try_emplace(result.first, 42, 420).second);
+}
+
+TEST(ListMapTest, try_emplace_back_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  auto result = list_map.try_emplace_back(42, 420);
+  EXPECT_TRUE(result.second);
+  auto iter = list_map.find(42);
+  EXPECT_EQ(iter->second, 420);
+  EXPECT_EQ(iter, result.first);
+  ASSERT_THAT(list_map, ElementsAre(Pair(1, 10), Pair(2, 20), Pair(42, 420)));
+  EXPECT_FALSE(list_map.try_emplace_back(42, 420).second);
+}
+
+TEST(ListMapTest, insert_at_position_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  auto iter = list_map.find(2);
+  EXPECT_EQ(iter->second, 20);
+  list_map.insert_or_assign(iter, 42, 420);
+  iter = list_map.find(42);
+  EXPECT_EQ(iter->second, 420);
+  ASSERT_THAT(list_map, ElementsAre(Pair(1, 10), Pair(42, 420), Pair(2, 20)));
+}
+
+TEST(ListMapTest, in_place_modification_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  auto iter = list_map.find(2);
+  iter->second = 200;
+  ASSERT_THAT(list_map, ElementsAre(Pair(1, 10), Pair(2, 200)));
+}
+
+TEST(ListMapTest, get_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  auto iter = list_map.find(1);
+  EXPECT_NE(iter, list_map.end());
+  EXPECT_EQ(iter->second, 10);
+}
+
+TEST(ListMapTest, remove_test) {
+  ListMap<int, int> list_map;
+  for (int key = 0; key <= 30; key++) {
+    list_map.insert_or_assign(key, key * 100);
+  }
+  for (int key = 0; key <= 30; key++) {
+    EXPECT_TRUE(list_map.contains(key));
+  }
+  for (int key = 0; key <= 30; key++) {
+    auto removed = list_map.extract(key);
+    EXPECT_TRUE(removed);
+    EXPECT_EQ(*removed, std::make_pair(key, key * 100));
+  }
+  for (int key = 0; key <= 30; key++) {
+    EXPECT_FALSE(list_map.contains(key));
+  }
+}
+
+TEST(ListMapTest, clear_test) {
+  ListMap<int, int> list_map;
+  for (int key = 0; key < 10; key++) {
+    list_map.insert_or_assign(key, key * 100);
+  }
+  for (int key = 0; key < 10; key++) {
+    EXPECT_TRUE(list_map.contains(key));
+  }
+  list_map.clear();
+  for (int key = 0; key < 10; key++) {
+    EXPECT_FALSE(list_map.contains(key));
+  }
+
+  for (int key = 0; key < 10; key++) {
+    list_map.insert_or_assign(key, key * 1000);
+  }
+  for (int key = 0; key < 10; key++) {
+    EXPECT_TRUE(list_map.contains(key));
+  }
+}
+
+TEST(ListMapTest, container_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  ASSERT_THAT(list_map, ElementsAre(Pair(1, 10), Pair(2, 20)));
+}
+
+TEST(ListMapTest, iterator_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  std::list<std::pair<int, int>> list(list_map.begin(), list_map.end());
+  ASSERT_THAT(list, ElementsAre(Pair(1, 10), Pair(2, 20)));
+}
+
+TEST(ListMapTest, for_loop_test) {
+  ListMap<int, int> list_map;
+  list_map.insert_or_assign(1, 10);
+  list_map.insert_or_assign(2, 20);
+  std::list<std::pair<int, int>> list;
+  for (const auto& node : list_map) {
+    list.emplace_back(node);
+  }
+  ASSERT_THAT(list, ElementsAre(Pair(1, 10), Pair(2, 20)));
+  list.clear();
+  for (auto& node : list_map) {
+    list.emplace_back(node);
+    node.second = node.second * 2;
+  }
+  ASSERT_THAT(list, ElementsAre(Pair(1, 10), Pair(2, 20)));
+  list.clear();
+  for (const auto& node : list_map) {
+    list.emplace_back(node);
+  }
+  ASSERT_THAT(list, ElementsAre(Pair(1, 20), Pair(2, 40)));
+}
+
+TEST(ListMapTest, pressure_test) {
+  auto started = std::chrono::high_resolution_clock::now();
+  int num_entries = 0xFFFF;  // 2^16 = 65535
+  ListMap<int, int> list_map;
+
+  // fill the list_map
+  for (int key = 0; key < num_entries; key++) {
+    list_map.insert_or_assign(key, key);
+  }
+
+  // make sure the list_map is full
+  for (int key = 0; key < num_entries; key++) {
+    EXPECT_TRUE(list_map.contains(key));
+  }
+
+  // clear the entire list_map
+  for (int key = 0; key < num_entries; key++) {
+    auto iter = list_map.find(key);
+    EXPECT_NE(iter, list_map.end());
+    EXPECT_EQ(iter->second, key);
+    EXPECT_TRUE(list_map.extract(key));
+  }
+  EXPECT_EQ(list_map.size(), 0);
+
+  // test execution time
+  auto done = std::chrono::high_resolution_clock::now();
+  int execution_time = std::chrono::duration_cast<std::chrono::microseconds>(done - started).count();
+  // Shouldn't be more than 1000ms
+  int execution_time_per_cycle_us = 10;
+  EXPECT_LT(execution_time, execution_time_per_cycle_us * num_entries);
+}
+
+}  // namespace testing
diff --git a/gd/common/lru_cache.h b/gd/common/lru_cache.h
new file mode 100644
index 0000000..632eaff
--- /dev/null
+++ b/gd/common/lru_cache.h
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <iterator>
+#include <list>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <unordered_map>
+
+#include "common/list_map.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace common {
+
+// An LRU map-cache the evict the oldest item when reaching capacity
+//
+// Usage:
+//   - keys are sorted from warmest to coldest
+//   - iterating through the cache won't warm up keys
+//   - operations on iterators won't warm up keys
+//   - find(), contains(), insert_or_assign() will warm up the key
+//   - insert_or_assign() will evict coldest key when cache reaches capacity
+//   - NOT THREAD SAFE
+//
+// Performance:
+//   - Key look-up and modification is O(1)
+//   - Memory consumption is:
+//     O(2*capacity*sizeof(K) + capacity*(sizeof(nullptr)+sizeof(V)))
+//
+// Template:
+//   - Key key type
+//   - T value type
+// */
+template <typename Key, typename T>
+class LruCache {
+ public:
+  using value_type = typename ListMap<Key, T>::value_type;
+  // different from c++17 node_type on purpose as we want node to be copyable
+  using node_type = typename ListMap<Key, T>::node_type;
+  using iterator = typename ListMap<Key, T>::iterator;
+  using const_iterator = typename ListMap<Key, T>::const_iterator;
+
+  // Constructor a LRU cache with |capacity|
+  explicit LruCache(size_t capacity) : capacity_(capacity) {
+    ASSERT_LOG(capacity_ != 0, "Unable to have 0 LRU Cache capacity");
+  }
+
+  // for move
+  LruCache(LruCache&& other) noexcept = default;
+  LruCache& operator=(LruCache&& other) noexcept = default;
+
+  // copy-constructor
+  // iterators in key_map_ cannot be copied directly
+  LruCache(const LruCache& other) : capacity_(other.capacity_), list_map_(other.list_map_) {}
+
+  // copy-assignment
+  // iterators in key_map_ cannot be copied directly
+  LruCache& operator=(const LruCache& other) {
+    if (&other == this) {
+      return *this;
+    }
+    capacity_ = other.capacity_;
+    list_map_ = other.list_map_;
+    return *this;
+  }
+
+  // comparison operators
+  bool operator==(const LruCache& rhs) const {
+    return capacity_ == rhs.capacity_ && list_map_ == rhs.list_map_;
+  }
+  bool operator!=(const LruCache& rhs) const {
+    return !(*this == rhs);
+  }
+
+  ~LruCache() {
+    clear();
+  }
+
+  // Clear the cache
+  void clear() {
+    list_map_.clear();
+  }
+
+  // Find the value of a key, and move the key to the head of cache, if there is one. Return iterator to value if key
+  // exists, end() if not. Iterator might be invalidated when removed or evicted. Const version.
+  //
+  // LRU: Will warm up key
+  // LRU: Access to returned iterator won't move key in LRU
+  const_iterator find(const Key& key) const {
+    return const_cast<LruCache*>(this)->find(key);
+  }
+
+  // Find the value of a key, and move the key to the head of cache, if there is one. Return iterator to value if key
+  // exists, end() if not. Iterator might be invalidated when removed or evicted
+  //
+  // LRU: Will warm up key
+  // LRU: Access to returned iterator won't move key in LRU
+  iterator find(const Key& key) {
+    auto iter = list_map_.find(key);
+    if (iter == list_map_.end()) {
+      return end();
+    }
+    // move to front
+    list_map_.splice(list_map_.begin(), list_map_, iter);
+    return iter;
+  }
+
+  // Check if key exist in the cache. Return true if key exist in cache, false, if not
+  //
+  // LRU: Will warm up key
+  bool contains(const Key& key) const {
+    return find(key) != list_map_.end();
+  }
+
+  // Put a key-value pair to the head of cache, evict the oldest key if cache is at capacity. Eviction is based on key
+  // ONLY. Hence, updating a key will not evict the oldest key. Return evicted value if old value was evicted,
+  // std::nullopt if not. The return value will be evaluated to true in a boolean context if a value is contained by
+  // std::optional, false otherwise.
+  //
+  // LRU: Will warm up key
+  std::optional<node_type> insert_or_assign(const Key& key, T value) {
+    if (contains(key)) {
+      // contains() calls find() that moved the node to the head
+      list_map_.begin()->second = std::move(value);
+      return std::nullopt;
+    }
+    // remove tail if at capacity
+    std::optional<node_type> evicted_node = std::nullopt;
+    if (list_map_.size() == capacity_) {
+      evicted_node = list_map_.extract(std::prev(list_map_.end())->first);
+    }
+    // insert new one to front of list
+    list_map_.insert_or_assign(list_map_.begin(), key, std::move(value));
+    return evicted_node;
+  }
+
+  // Put a key-value pair to the head of cache, evict the oldest key if cache is at capacity. Eviction is based on key
+  // ONLY. Hence, updating a key will not evict the oldest key. This method tries to construct the value in-place. If
+  // the key already exist, this method only update the value. Return inserted iterator, whether insertion happens, and
+  // evicted value if old value was evicted or std::nullopt
+  //
+  // LRU: Will warm up key
+  template <class... Args>
+  std::tuple<iterator, bool, std::optional<node_type>> try_emplace(const Key& key, Args&&... args) {
+    if (contains(key)) {
+      // contains() calls find() that moved the node to the head
+      return std::make_tuple(end(), false, std::nullopt);
+    }
+    // remove tail if at capacity
+    std::optional<node_type> evicted_node = std::nullopt;
+    if (list_map_.size() == capacity_) {
+      evicted_node = list_map_.extract(std::prev(list_map_.end())->first);
+    }
+    // insert new one to front of list
+    auto pair = list_map_.try_emplace(list_map_.begin(), key, std::forward<Args>(args)...);
+    return std::make_tuple(pair.first, pair.second, std::move(evicted_node));
+  }
+
+  // Delete a key from cache, return removed value if old value was evicted, std::nullopt if not. The return value will
+  // be evaluated to true in a boolean context if a value is contained by std::optional, false otherwise.
+  inline std::optional<node_type> extract(const Key& key) {
+    return list_map_.extract(key);
+  }
+
+  /// Remove an iterator pointed item from the lru cache and return the iterator immediately after the erased item
+  iterator erase(const_iterator iter) {
+    return list_map_.erase(iter);
+  }
+
+  // Return size of the cache
+  inline size_t size() const {
+    return list_map_.size();
+  }
+
+  // Iterator interface for begin
+  inline iterator begin() {
+    return list_map_.begin();
+  }
+
+  // Return iterator interface for begin, const
+  inline const_iterator begin() const {
+    return list_map_.begin();
+  }
+
+  // Return iterator interface for end
+  inline iterator end() {
+    return list_map_.end();
+  }
+
+  // Iterator interface for end, const
+  inline const_iterator end() const {
+    return list_map_.end();
+  }
+
+ private:
+  size_t capacity_;
+  ListMap<Key, T> list_map_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/lru_cache_test.cc b/gd/common/lru_cache_test.cc
new file mode 100644
index 0000000..0980f88
--- /dev/null
+++ b/gd/common/lru_cache_test.cc
@@ -0,0 +1,461 @@
+/*
+ * 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.
+ */
+
+#include <chrono>
+#include <limits>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "common/lru_cache.h"
+
+namespace testing {
+
+using bluetooth::common::LruCache;
+
+TEST(LruCacheTest, empty_test) {
+  LruCache<int, int> cache(3);  // capacity = 3;
+  EXPECT_EQ(cache.size(), 0);
+  EXPECT_EQ(cache.find(42), cache.end());
+  cache.clear();  // should not crash
+  EXPECT_EQ(cache.find(42), cache.end());
+  EXPECT_FALSE(cache.contains(42));
+  EXPECT_FALSE(cache.extract(42));
+}
+
+TEST(LruCacheTest, comparison_test) {
+  LruCache<int, int> cache_1(2);
+  cache_1.insert_or_assign(1, 10);
+  cache_1.insert_or_assign(2, 20);
+  LruCache<int, int> cache_2(2);
+  cache_2.insert_or_assign(1, 10);
+  cache_2.insert_or_assign(2, 20);
+  EXPECT_EQ(cache_1, cache_2);
+  // Cache with different order should not be equal
+  cache_2.find(1);
+  EXPECT_NE(cache_1, cache_2);
+  cache_1.find(1);
+  EXPECT_EQ(cache_1, cache_2);
+  // Cache with different value should be different
+  cache_2.insert_or_assign(1, 11);
+  EXPECT_NE(cache_1, cache_2);
+  // Cache with different capacity should not be equal
+  LruCache<int, int> cache_3(3);
+  cache_3.insert_or_assign(1, 10);
+  cache_3.insert_or_assign(2, 20);
+  EXPECT_NE(cache_1, cache_3);
+  // Empty cache should not be equal to non-empty ones
+  LruCache<int, int> cache_4(2);
+  EXPECT_NE(cache_1, cache_4);
+  // Empty caches should be equal
+  LruCache<int, int> cache_5(2);
+  EXPECT_EQ(cache_4, cache_5);
+  // Empty caches with different capacity should not be equal
+  LruCache<int, int> cache_6(3);
+  EXPECT_NE(cache_4, cache_6);
+}
+
+TEST(LruCacheTest, try_emplace_test) {
+  LruCache<int, int> cache(2);
+  cache.insert_or_assign(1, 10);
+  cache.insert_or_assign(2, 20);
+  auto result = cache.try_emplace(42, 420);
+  // 1, 10 evicted
+  EXPECT_EQ(std::get<2>(result), std::make_pair(1, 10));
+  auto iter = cache.find(42);
+  EXPECT_EQ(iter->second, 420);
+  EXPECT_EQ(iter, std::get<0>(result));
+  ASSERT_THAT(cache, ElementsAre(Pair(42, 420), Pair(2, 20)));
+}
+
+TEST(LruCacheTest, copy_test) {
+  LruCache<int, std::shared_ptr<int>> cache(2);
+  cache.insert_or_assign(1, std::make_shared<int>(100));
+  auto iter = cache.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  LruCache<int, std::shared_ptr<int>> new_cache = cache;
+  iter = new_cache.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  *iter->second = 300;
+  iter = new_cache.find(1);
+  EXPECT_EQ(*iter->second, 300);
+  // Since copy is used, shared_ptr should increase count
+  EXPECT_EQ(iter->second.use_count(), 2);
+}
+
+TEST(LruCacheTest, move_test) {
+  LruCache<int, std::shared_ptr<int>> cache(2);
+  cache.insert_or_assign(1, std::make_shared<int>(100));
+  auto iter = cache.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  LruCache<int, std::shared_ptr<int>> new_cache = std::move(cache);
+  iter = new_cache.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  *iter->second = 300;
+  iter = new_cache.find(1);
+  EXPECT_EQ(*iter->second, 300);
+  // Since move is used, shared_ptr should not increase count
+  EXPECT_EQ(iter->second.use_count(), 1);
+}
+
+TEST(LruCacheTest, move_insert_unique_ptr_test) {
+  LruCache<int, std::unique_ptr<int>> cache(2);
+  cache.insert_or_assign(1, std::make_unique<int>(100));
+  auto iter = cache.find(1);
+  EXPECT_EQ(*iter->second, 100);
+  cache.insert_or_assign(1, std::make_unique<int>(400));
+  iter = cache.find(1);
+  EXPECT_EQ(*iter->second, 400);
+}
+
+TEST(LruCacheTest, move_insert_cache_test) {
+  LruCache<int, LruCache<int, int>> cache(2);
+  LruCache<int, int> m1(2);
+  m1.insert_or_assign(1, 100);
+  cache.insert_or_assign(1, std::move(m1));
+  auto iter = cache.find(1);
+  EXPECT_THAT(iter->second, ElementsAre(Pair(1, 100)));
+  LruCache<int, int> m2(2);
+  m2.insert_or_assign(2, 200);
+  cache.insert_or_assign(1, std::move(m2));
+  iter = cache.find(1);
+  EXPECT_THAT(iter->second, ElementsAre(Pair(2, 200)));
+}
+
+TEST(LruCacheTest, erase_one_item_test) {
+  LruCache<int, int> cache(3);
+  cache.insert_or_assign(1, 10);
+  cache.insert_or_assign(2, 20);
+  cache.insert_or_assign(3, 30);
+  auto iter = cache.find(2);
+  // 2, 3, 1
+  cache.find(3);
+  // 3, 2, 1
+  iter = cache.erase(iter);
+  EXPECT_EQ(iter->first, 1);
+  EXPECT_EQ(iter->second, 10);
+  EXPECT_THAT(cache, ElementsAre(Pair(3, 30), Pair(1, 10)));
+}
+
+TEST(LruCacheTest, erase_in_for_loop_test) {
+  LruCache<int, int> cache(3);
+  cache.insert_or_assign(1, 10);
+  cache.insert_or_assign(2, 20);
+  cache.insert_or_assign(3, 30);
+  for (auto iter = cache.begin(); iter != cache.end();) {
+    if (iter->first == 2) {
+      iter = cache.erase(iter);
+    } else {
+      ++iter;
+    }
+  }
+  EXPECT_THAT(cache, ElementsAre(Pair(3, 30), Pair(1, 10)));
+}
+
+TEST(LruCacheTest, get_and_contains_key_test) {
+  LruCache<int, int> cache(3);  // capacity = 3;
+  EXPECT_EQ(cache.size(), 0);
+  EXPECT_EQ(cache.find(42), cache.end());
+  EXPECT_FALSE(cache.contains(42));
+  EXPECT_FALSE(cache.insert_or_assign(56, 200));
+  EXPECT_EQ(cache.find(42), cache.end());
+  EXPECT_FALSE(cache.contains(42));
+  EXPECT_NE(cache.find(56), cache.end());
+  EXPECT_TRUE(cache.contains(56));
+  auto iter = cache.find(56);
+  EXPECT_NE(iter, cache.end());
+  EXPECT_EQ(iter->second, 200);
+  EXPECT_TRUE(cache.extract(56));
+  EXPECT_FALSE(cache.contains(56));
+}
+
+TEST(LruCacheTest, put_and_get_sequence_1) {
+  // Section 1: Ordered put and ordered get
+  LruCache<int, int> cache(3);  // capacity = 3;
+  EXPECT_FALSE(cache.insert_or_assign(1, 10));
+  EXPECT_EQ(cache.size(), 1);
+  EXPECT_FALSE(cache.insert_or_assign(2, 20));
+  EXPECT_EQ(cache.size(), 2);
+  EXPECT_FALSE(cache.insert_or_assign(3, 30));
+  EXPECT_EQ(cache.size(), 3);
+  // 3, 2, 1 after above operations
+
+  auto evicted = cache.insert_or_assign(4, 40);
+  // 4, 3, 2 after above operations, 1 is evicted
+  EXPECT_TRUE(evicted);
+  EXPECT_EQ(*evicted, std::make_pair(1, 10));
+  EXPECT_EQ(cache.find(1), cache.end());
+  LruCache<int, int>::const_iterator iter;
+  EXPECT_NE(iter = cache.find(4), cache.end());
+  EXPECT_EQ(iter->second, 40);
+  EXPECT_NE(iter = cache.find(2), cache.end());
+  EXPECT_EQ(iter->second, 20);
+  EXPECT_NE(iter = cache.find(3), cache.end());
+  EXPECT_EQ(iter->second, 30);
+  // 3, 2, 4 after above operations
+
+  // Section 2: Over capacity put and ordered get
+  evicted = cache.insert_or_assign(5, 50);
+  // 5, 3, 2 after above operations, 4 is evicted
+  EXPECT_EQ(cache.size(), 3);
+  EXPECT_TRUE(evicted);
+  EXPECT_EQ(*evicted, std::make_pair(4, 40));
+
+  EXPECT_TRUE(cache.extract(3));
+  // 5, 2 should be in cache, 3 is removed
+  EXPECT_FALSE(cache.insert_or_assign(6, 60));
+  // 6, 5, 2 should be in cache
+
+  // Section 3: Out of order get
+  EXPECT_EQ(cache.find(3), cache.end());
+  EXPECT_EQ(cache.find(4), cache.end());
+  EXPECT_NE(iter = cache.find(2), cache.end());
+  // 2, 6, 5 should be in cache
+  EXPECT_EQ(iter->second, 20);
+  EXPECT_NE(iter = cache.find(6), cache.end());
+  // 6, 2, 5 should be in cache
+  EXPECT_EQ(iter->second, 60);
+  EXPECT_NE(iter = cache.find(5), cache.end());
+  // 5, 6, 2 should be in cache
+  EXPECT_EQ(iter->second, 50);
+  evicted = cache.insert_or_assign(7, 70);
+  // 7, 5, 6 should be in cache, 2 is evicted
+  EXPECT_TRUE(evicted);
+  EXPECT_EQ(*evicted, std::make_pair(2, 20));
+}
+
+TEST(LruCacheTest, put_and_get_sequence_2) {
+  // Section 1: Replace item in cache
+  LruCache<int, int> cache(2);  // size = 2;
+  EXPECT_FALSE(cache.insert_or_assign(1, 10));
+  EXPECT_FALSE(cache.insert_or_assign(2, 20));
+  // 2, 1 in cache
+  auto evicted = cache.insert_or_assign(3, 30);
+  // 3, 2 in cache, 1 is evicted
+  EXPECT_TRUE(evicted);
+  EXPECT_EQ(*evicted, std::make_pair(1, 10));
+  EXPECT_FALSE(cache.insert_or_assign(2, 200));
+  // 2, 3 in cache, nothing is evicted
+  EXPECT_EQ(cache.size(), 2);
+
+  EXPECT_FALSE(cache.contains(1));
+  LruCache<int, int>::const_iterator iter;
+  EXPECT_NE(iter = cache.find(2), cache.end());
+  EXPECT_EQ(iter->second, 200);
+  EXPECT_NE(iter = cache.find(3), cache.end());
+  // 3, 2 in cache
+  EXPECT_EQ(iter->second, 30);
+
+  evicted = cache.insert_or_assign(4, 40);
+  // 4, 3 in cache, 2 is evicted
+  EXPECT_TRUE(evicted);
+  EXPECT_EQ(*evicted, std::make_pair(2, 200));
+
+  EXPECT_FALSE(cache.contains(2));
+  EXPECT_NE(iter = cache.find(3), cache.end());
+  EXPECT_EQ(iter->second, 30);
+  EXPECT_NE(iter = cache.find(4), cache.end());
+  EXPECT_EQ(iter->second, 40);
+  // 4, 3 in cache
+
+  EXPECT_TRUE(cache.extract(4));
+  EXPECT_FALSE(cache.contains(4));
+  // 3 in cache
+  EXPECT_EQ(cache.size(), 1);
+  EXPECT_FALSE(cache.insert_or_assign(2, 2000));
+  // 2, 3 in cache
+
+  EXPECT_FALSE(cache.contains(4));
+  EXPECT_NE(iter = cache.find(3), cache.end());
+  EXPECT_EQ(iter->second, 30);
+  EXPECT_NE(iter = cache.find(2), cache.end());
+  EXPECT_EQ(iter->second, 2000);
+
+  EXPECT_TRUE(cache.extract(2));
+  EXPECT_TRUE(cache.extract(3));
+  EXPECT_FALSE(cache.insert_or_assign(5, 50));
+  EXPECT_FALSE(cache.insert_or_assign(1, 100));
+  EXPECT_FALSE(cache.insert_or_assign(5, 1000));
+  EXPECT_EQ(cache.size(), 2);
+  // 5, 1 in cache
+
+  evicted = cache.insert_or_assign(6, 2000);
+  // 6, 5 in cache
+  EXPECT_TRUE(evicted);
+  EXPECT_EQ(*evicted, std::make_pair(1, 100));
+
+  EXPECT_FALSE(cache.contains(2));
+  EXPECT_FALSE(cache.contains(3));
+  EXPECT_NE(iter = cache.find(6), cache.end());
+  EXPECT_EQ(iter->second, 2000);
+  EXPECT_NE(iter = cache.find(5), cache.end());
+  EXPECT_EQ(iter->second, 1000);
+}
+
+TEST(LruCacheTest, in_place_modification_test) {
+  LruCache<int, int> cache(2);
+  cache.insert_or_assign(1, 10);
+  cache.insert_or_assign(2, 20);
+  auto iter = cache.find(2);
+  ASSERT_THAT(cache, ElementsAre(Pair(2, 20), Pair(1, 10)));
+  iter->second = 200;
+  ASSERT_THAT(cache, ElementsAre(Pair(2, 200), Pair(1, 10)));
+  cache.insert_or_assign(1, 100);
+  // 1, 2 in cache
+  ASSERT_THAT(cache, ElementsAre(Pair(1, 100), Pair(2, 200)));
+  // modifying iterator does not warm up key
+  iter->second = 400;
+  ASSERT_THAT(cache, ElementsAre(Pair(1, 100), Pair(2, 400)));
+}
+
+TEST(LruCacheTest, get_test) {
+  LruCache<int, int> cache(2);
+  EXPECT_FALSE(cache.insert_or_assign(1, 10));
+  EXPECT_FALSE(cache.insert_or_assign(2, 20));
+  EXPECT_TRUE(cache.contains(1));
+  // 1, 2 in cache
+  auto evicted = cache.insert_or_assign(3, 30);
+  // 3, 1 in cache
+  EXPECT_TRUE(evicted);
+  EXPECT_EQ(*evicted, std::make_pair(2, 20));
+}
+
+TEST(LruCacheTest, remove_test) {
+  LruCache<int, int> cache(10);
+  for (int key = 0; key <= 30; key++) {
+    cache.insert_or_assign(key, key * 100);
+  }
+  for (int key = 0; key <= 20; key++) {
+    EXPECT_FALSE(cache.contains(key));
+  }
+  for (int key = 21; key <= 30; key++) {
+    EXPECT_TRUE(cache.contains(key));
+  }
+  for (int key = 0; key <= 20; key++) {
+    EXPECT_FALSE(cache.extract(key));
+  }
+  for (int key = 21; key <= 30; key++) {
+    auto removed = cache.extract(key);
+    EXPECT_TRUE(removed);
+    EXPECT_EQ(*removed, std::make_pair(key, key * 100));
+  }
+  for (int key = 21; key <= 30; key++) {
+    EXPECT_FALSE(cache.contains(key));
+  }
+}
+
+TEST(LruCacheTest, clear_test) {
+  LruCache<int, int> cache(10);
+  for (int key = 0; key < 10; key++) {
+    cache.insert_or_assign(key, key * 100);
+  }
+  for (int key = 0; key < 10; key++) {
+    EXPECT_TRUE(cache.contains(key));
+  }
+  cache.clear();
+  for (int key = 0; key < 10; key++) {
+    EXPECT_FALSE(cache.contains(key));
+  }
+
+  for (int key = 0; key < 10; key++) {
+    cache.insert_or_assign(key, key * 1000);
+  }
+  for (int key = 0; key < 10; key++) {
+    EXPECT_TRUE(cache.contains(key));
+  }
+}
+
+TEST(LruCacheTest, container_test) {
+  LruCache<int, int> lru_cache(2);
+  lru_cache.insert_or_assign(1, 10);
+  lru_cache.insert_or_assign(2, 20);
+  // Warm elements first
+  ASSERT_THAT(lru_cache, ElementsAre(Pair(2, 20), Pair(1, 10)));
+}
+
+TEST(LruCacheTest, iterator_test) {
+  LruCache<int, int> lru_cache(2);
+  lru_cache.insert_or_assign(1, 10);
+  lru_cache.insert_or_assign(2, 20);
+  // Warm elements first
+  std::list<std::pair<int, int>> list(lru_cache.begin(), lru_cache.end());
+  ASSERT_THAT(list, ElementsAre(Pair(2, 20), Pair(1, 10)));
+}
+
+TEST(LruCacheTest, for_loop_test) {
+  LruCache<int, int> lru_cache(2);
+  lru_cache.insert_or_assign(1, 10);
+  lru_cache.insert_or_assign(2, 20);
+  // Warm elements first
+  std::list<std::pair<int, int>> list;
+  for (const auto& node : lru_cache) {
+    list.emplace_back(node);
+  }
+  ASSERT_THAT(list, ElementsAre(Pair(2, 20), Pair(1, 10)));
+  list.clear();
+  for (auto& node : lru_cache) {
+    list.emplace_back(node);
+    node.second = node.second * 2;
+  }
+  ASSERT_THAT(list, ElementsAre(Pair(2, 20), Pair(1, 10)));
+  list.clear();
+  for (const auto& node : lru_cache) {
+    list.emplace_back(node);
+  }
+  ASSERT_THAT(list, ElementsAre(Pair(2, 40), Pair(1, 20)));
+}
+
+TEST(LruCacheTest, pressure_test) {
+  auto started = std::chrono::high_resolution_clock::now();
+  int capacity = 0xFFFF;  // 2^16 = 65535
+  LruCache<int, int> cache(static_cast<size_t>(capacity));
+
+  // fill the cache
+  for (int key = 0; key < capacity; key++) {
+    cache.insert_or_assign(key, key);
+  }
+
+  // make sure the cache is full
+  for (int key = 0; key < capacity; key++) {
+    EXPECT_TRUE(cache.contains(key));
+  }
+
+  // refresh the entire cache
+  for (int key = 0; key < capacity; key++) {
+    int new_key = key + capacity;
+    cache.insert_or_assign(new_key, new_key);
+    EXPECT_FALSE(cache.contains(key));
+    EXPECT_TRUE(cache.contains(new_key));
+  }
+
+  // clear the entire cache
+  LruCache<int, int>::const_iterator iter;
+  for (int key = capacity; key < 2 * capacity; key++) {
+    EXPECT_NE(iter = cache.find(key), cache.end());
+    EXPECT_EQ(iter->second, key);
+    EXPECT_TRUE(cache.extract(key));
+  }
+  EXPECT_EQ(cache.size(), 0);
+
+  // test execution time
+  auto done = std::chrono::high_resolution_clock::now();
+  int execution_time = std::chrono::duration_cast<std::chrono::microseconds>(done - started).count();
+  // Shouldn't be more than 1000ms
+  int execution_time_per_cycle_us = 15;
+  EXPECT_LT(execution_time, execution_time_per_cycle_us * capacity);
+}
+
+}  // namespace testing
diff --git a/gd/common/strings.cc b/gd/common/strings.cc
new file mode 100644
index 0000000..e1ff8d4
--- /dev/null
+++ b/gd/common/strings.cc
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#include "common/strings.h"
+
+#include <charconv>
+#include <functional>
+#include <iomanip>
+#include <sstream>
+#include <system_error>
+
+#include "os/log.h"
+
+namespace {
+
+struct IsSpace : std::unary_function<std::string::value_type, bool> {
+  bool operator()(std::string::value_type v) {
+    return isspace(static_cast<int>(v));
+  }
+};
+
+struct IsHexDigit : std::unary_function<std::string::value_type, bool> {
+  bool operator()(std::string::value_type v) {
+    return isxdigit(static_cast<int>(v));
+  }
+};
+
+}  // namespace
+
+namespace bluetooth {
+namespace common {
+
+std::string ToHexString(const std::vector<uint8_t>& value) {
+  std::stringstream ss;
+  for (const auto& byte : value) {
+    ss << std::hex << std::setw(2) << std::setfill('0') << +byte;
+  }
+  return ss.str();
+}
+
+std::optional<std::vector<uint8_t>> FromHexString(const std::string& str) {
+  if (str.size() % 2 != 0) {
+    LOG_DEBUG("str size is not divisible by 2, size is %zu", str.size());
+    return std::nullopt;
+  }
+  if (std::find_if_not(str.begin(), str.end(), IsHexDigit{}) != str.end()) {
+    LOG_DEBUG("value contains none hex digit");
+    return std::nullopt;
+  }
+  std::vector<uint8_t> value;
+  value.reserve(str.size() / 2);
+  for (size_t i = 0; i < str.size(); i += 2) {
+    uint8_t v = 0;
+    auto ret = std::from_chars(str.c_str() + i, str.c_str() + i + 2, v, 16);
+    if (std::make_error_code(ret.ec)) {
+      LOG_DEBUG("failed to parse hex char at index %zu", i);
+      return std::nullopt;
+    }
+    value.push_back(v);
+  }
+  return value;
+}
+
+std::string StringTrim(std::string str) {
+  str.erase(str.begin(), std::find_if_not(str.begin(), str.end(), IsSpace{}));
+  str.erase(std::find_if_not(str.rbegin(), str.rend(), IsSpace{}).base(), str.end());
+  return str;
+}
+
+std::vector<std::string> StringSplit(const std::string& str, const std::string& delim, size_t max_token) {
+  ASSERT_LOG(!delim.empty(), "delim cannot be empty");
+  std::vector<std::string> tokens;
+  // Use std::string::find and std::string::substr to avoid copying str into a stringstream
+  std::string::size_type starting_index = 0;
+  auto index_of_delim = str.find(delim);
+  while ((max_token == 0 || tokens.size() < (max_token - 1)) && index_of_delim != std::string::npos) {
+    tokens.push_back(str.substr(starting_index, index_of_delim - starting_index));
+    starting_index = index_of_delim + delim.size();
+    index_of_delim = str.find(delim, starting_index);
+  }
+  // Append last item to the vector if there are anything left
+  if (starting_index < (str.size() + 1)) {
+    tokens.push_back(str.substr(starting_index));
+  }
+  return tokens;
+}
+
+}  // namespace common
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/common/strings.h b/gd/common/strings.h
new file mode 100644
index 0000000..088616c
--- /dev/null
+++ b/gd/common/strings.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace bluetooth {
+namespace common {
+
+// Convert value into a hex decimal formatted string in lower case
+std::string ToHexString(const std::vector<uint8_t>& value);
+
+// Parse |str| into a std::vector<uint8_t>, |str| must contains only hex decimal
+std::optional<std::vector<uint8_t>> FromHexString(const std::string& str);
+
+// Remove whitespace from both ends of the |str|, returning a copy
+std::string StringTrim(std::string str);
+
+// Split |str| into at most |max_token| tokens delimited by |delim|, unlimited tokens when |max_token| is 0
+std::vector<std::string> StringSplit(const std::string& str, const std::string& delim, size_t max_token = 0);
+
+}  // namespace common
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/common/strings_test.cc b/gd/common/strings_test.cc
new file mode 100644
index 0000000..9c44f43
--- /dev/null
+++ b/gd/common/strings_test.cc
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include "common/strings.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+
+namespace testing {
+
+using bluetooth::common::FromHexString;
+using bluetooth::common::StringSplit;
+using bluetooth::common::StringTrim;
+using bluetooth::common::ToHexString;
+
+TEST(StringsTest, trim_string_test) {
+  EXPECT_EQ(StringTrim("  aa bb"), "aa bb");
+  EXPECT_EQ(StringTrim("aa bb "), "aa bb");
+  EXPECT_EQ(StringTrim("  aa bb "), "aa bb");
+  EXPECT_EQ(StringTrim("  aa bb \n"), "aa bb");
+  EXPECT_EQ(StringTrim("  \raa bb\t \n"), "aa bb");
+}
+
+TEST(StringsTest, split_string_test) {
+  EXPECT_THAT(StringSplit("", ","), ElementsAre(""));
+  EXPECT_THAT(StringSplit("1,2,3", ","), ElementsAre("1", "2", "3"));
+  EXPECT_THAT(StringSplit("1,2,3", "!"), ElementsAre("1,2,3"));
+  EXPECT_THAT(StringSplit("1,2,3", ",", 2), ElementsAre("1", "2,3"));
+  EXPECT_THAT(StringSplit("a,b,", ","), ElementsAre("a", "b", ""));
+  EXPECT_THAT(StringSplit("ab,", ",", 2), ElementsAre("ab", ""));
+  EXPECT_THAT(StringSplit("ab,,", ",", 2), ElementsAre("ab", ","));
+  EXPECT_THAT(StringSplit("ab,,", ",", 1), ElementsAre("ab,,"));
+  EXPECT_THAT(StringSplit("1,,2,,3", ",,"), ElementsAre("1", "2", "3"));
+  EXPECT_THAT(StringSplit("1,,2,,3,,", ",,"), ElementsAre("1", "2", "3", ""));
+  EXPECT_THAT(StringSplit("1,,2,,3,,", ",,", 2), ElementsAre("1", "2,,3,,"));
+  EXPECT_THAT(StringSplit("1", ",,", 2), ElementsAre("1"));
+  EXPECT_DEATH({ StringSplit("1,2,3", ""); }, "delim cannot be empty");
+}
+
+TEST(StringsTest, to_hex_string_test) {
+  // normal
+  EXPECT_THAT(ToHexString({0x12, 0x34, 0x56, 0xab}), Eq("123456ab"));
+  // empty
+  EXPECT_THAT(ToHexString({}), Eq(""));
+  // unary
+  EXPECT_THAT(ToHexString({0x12}), Eq("12"));
+  // half
+  EXPECT_THAT(ToHexString({0x6, 0x5, 0x56, 0xb}), Eq("0605560b"));
+}
+
+TEST(StringsTest, from_hex_string_test) {
+  // normal
+  EXPECT_THAT(FromHexString("aabbccdd1122"), Optional(ElementsAre(0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22)));
+  // empty
+  EXPECT_THAT(FromHexString(""), Optional(IsEmpty()));
+  // unary
+  EXPECT_THAT(FromHexString("aa"), Optional(ElementsAre(0xaa)));
+  // half
+  EXPECT_THAT(FromHexString("0605560b"), Optional(ElementsAre(0x6, 0x5, 0x56, 0xb)));
+  // upper case letter
+  EXPECT_THAT(FromHexString("AABBCC"), Optional(ElementsAre(0xaa, 0xbb, 0xcc)));
+  // upper and lower case letter mixed
+  EXPECT_THAT(FromHexString("aAbbCC"), Optional(ElementsAre(0xaa, 0xbb, 0xcc)));
+  // Error: odd length
+  EXPECT_FALSE(FromHexString("0605560"));
+  // Error: non hex char
+  EXPECT_FALSE(FromHexString("060u560b"));
+}
+
+}  // namespace testing
\ No newline at end of file
diff --git a/gd/crypto_toolbox/aes.cc b/gd/crypto_toolbox/aes.cc
index f53894e..395642c 100644
--- a/gd/crypto_toolbox/aes.cc
+++ b/gd/crypto_toolbox/aes.cc
@@ -77,11 +77,8 @@
 
 #define f1(x) (x)
 #define f2(x) (((x) << 1) ^ ((((x) >> 7) & 1) * WPOLY))
-#define f4(x) \
-  (((x) << 2) ^ ((((x) >> 6) & 1) * WPOLY) ^ ((((x) >> 6) & 2) * WPOLY))
-#define f8(x)                                                             \
-  (((x) << 3) ^ ((((x) >> 5) & 1) * WPOLY) ^ ((((x) >> 5) & 2) * WPOLY) ^ \
-   ((((x) >> 5) & 4) * WPOLY))
+#define f4(x) (((x) << 2) ^ ((((x) >> 6) & 1) * WPOLY) ^ ((((x) >> 6) & 2) * WPOLY))
+#define f8(x) (((x) << 3) ^ ((((x) >> 5) & 1) * WPOLY) ^ ((((x) >> 5) & 2) * WPOLY) ^ ((((x) >> 5) & 4) * WPOLY))
 #define d2(x) (((x) >> 1) ^ ((x)&1 ? DPOLY : 0))
 
 #define f3(x) (f2(x) ^ (x))
@@ -92,127 +89,82 @@
 
 #if defined(USE_TABLES)
 
-#define sb_data(w)                                                          \
-  { /* S Box data values */                                                 \
-    w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5), \
-        w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab),      \
-        w(0x76), w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59),      \
-        w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c),      \
-        w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), w(0x26),      \
-        w(0x36), w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), w(0xe5),      \
-        w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15), w(0x04), w(0xc7),      \
-        w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a), w(0x07),      \
-        w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),      \
-        w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a),      \
-        w(0xa0), w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3),      \
-        w(0x2f), w(0x84), w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20),      \
-        w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), w(0x39),      \
-        w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), w(0xaa),      \
-        w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), w(0xf9),      \
-        w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8), w(0x51),      \
-        w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),      \
-        w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3),      \
-        w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97),      \
-        w(0x44), w(0x17), w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64),      \
-        w(0x5d), w(0x19), w(0x73), w(0x60), w(0x81), w(0x4f), w(0xdc),      \
-        w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), w(0xb8),      \
-        w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), w(0x32),      \
-        w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), w(0xc2),      \
-        w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),      \
-        w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e),      \
-        w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a),      \
-        w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c),      \
-        w(0xa6), w(0xb4), w(0xc6), w(0xe8), w(0xdd), w(0x74), w(0x1f),      \
-        w(0x4b), w(0xbd), w(0x8b), w(0x8a), w(0x70), w(0x3e), w(0xb5),      \
-        w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), w(0x35),      \
-        w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), w(0xe1),      \
-        w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),      \
-        w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28),      \
-        w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6),      \
-        w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0),      \
-        w(0x54), w(0xbb), w(0x16)                                           \
+#define sb_data(w)                                                                                                  \
+  { /* S Box data values */                                                                                         \
+    w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5), w(0x30), w(0x01), w(0x67), w(0x2b),     \
+        w(0xfe), w(0xd7), w(0xab), w(0x76), w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0), \
+        w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), w(0x26), \
+        w(0x36), w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15), \
+        w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a), w(0x07), w(0x12), w(0x80), w(0xe2), \
+        w(0xeb), w(0x27), w(0xb2), w(0x75), w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0), \
+        w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84), w(0x53), w(0xd1), w(0x00), w(0xed), \
+        w(0x20), w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), \
+        w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), w(0xf9), w(0x02), w(0x7f), \
+        w(0x50), w(0x3c), w(0x9f), w(0xa8), w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5), \
+        w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), \
+        w(0x5f), w(0x97), w(0x44), w(0x17), w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73), \
+        w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), w(0xb8), w(0x14), \
+        w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), \
+        w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79), w(0xe7), w(0xc8), w(0x37), w(0x6d), \
+        w(0x8d), w(0xd5), w(0x4e), w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08), \
+        w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6), w(0xe8), w(0xdd), w(0x74), w(0x1f), \
+        w(0x4b), w(0xbd), w(0x8b), w(0x8a), w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), \
+        w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), w(0xe1), w(0xf8), w(0x98), w(0x11), \
+        w(0x69), w(0xd9), w(0x8e), w(0x94), w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf), \
+        w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), \
+        w(0xb0), w(0x54), w(0xbb), w(0x16)                                                                          \
   }
 
-#define isb_data(w)                                                         \
-  { /* inverse S Box data values */                                         \
-    w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38), \
-        w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7),      \
-        w(0xfb), w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f),      \
-        w(0xff), w(0x87), w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4),      \
-        w(0xde), w(0xe9), w(0xcb), w(0x54), w(0x7b), w(0x94), w(0x32),      \
-        w(0xa6), w(0xc2), w(0x23), w(0x3d), w(0xee), w(0x4c), w(0x95),      \
-        w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e), w(0x08), w(0x2e),      \
-        w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2), w(0x76),      \
-        w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),      \
-        w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98),      \
-        w(0x16), w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65),      \
-        w(0xb6), w(0x92), w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd),      \
-        w(0xed), w(0xb9), w(0xda), w(0x5e), w(0x15), w(0x46), w(0x57),      \
-        w(0xa7), w(0x8d), w(0x9d), w(0x84), w(0x90), w(0xd8), w(0xab),      \
-        w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a), w(0xf7), w(0xe4),      \
-        w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06), w(0xd0),      \
-        w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),      \
-        w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a),      \
-        w(0x6b), w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67),      \
-        w(0xdc), w(0xea), w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0),      \
-        w(0xb4), w(0xe6), w(0x73), w(0x96), w(0xac), w(0x74), w(0x22),      \
-        w(0xe7), w(0xad), w(0x35), w(0x85), w(0xe2), w(0xf9), w(0x37),      \
-        w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e), w(0x47), w(0xf1),      \
-        w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89), w(0x6f),      \
-        w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),      \
-        w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79),      \
-        w(0x20), w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd),      \
-        w(0x5a), w(0xf4), w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88),      \
-        w(0x07), w(0xc7), w(0x31), w(0xb1), w(0x12), w(0x10), w(0x59),      \
-        w(0x27), w(0x80), w(0xec), w(0x5f), w(0x60), w(0x51), w(0x7f),      \
-        w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d), w(0x2d), w(0xe5),      \
-        w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef), w(0xa0),      \
-        w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),      \
-        w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99),      \
-        w(0x61), w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77),      \
-        w(0xd6), w(0x26), w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55),      \
-        w(0x21), w(0x0c), w(0x7d)                                           \
+#define isb_data(w)                                                                                                 \
+  { /* inverse S Box data values */                                                                                 \
+    w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38), w(0xbf), w(0x40), w(0xa3), w(0x9e),     \
+        w(0x81), w(0xf3), w(0xd7), w(0xfb), w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87), \
+        w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb), w(0x54), w(0x7b), w(0x94), w(0x32), \
+        w(0xa6), w(0xc2), w(0x23), w(0x3d), w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e), \
+        w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2), w(0x76), w(0x5b), w(0xa2), w(0x49), \
+        w(0x6d), w(0x8b), w(0xd1), w(0x25), w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16), \
+        w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92), w(0x6c), w(0x70), w(0x48), w(0x50), \
+        w(0xfd), w(0xed), w(0xb9), w(0xda), w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84), \
+        w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a), w(0xf7), w(0xe4), w(0x58), w(0x05), \
+        w(0xb8), w(0xb3), w(0x45), w(0x06), w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02), \
+        w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b), w(0x3a), w(0x91), w(0x11), w(0x41), \
+        w(0x4f), w(0x67), w(0xdc), w(0xea), w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73), \
+        w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85), w(0xe2), w(0xf9), w(0x37), w(0xe8), \
+        w(0x1c), w(0x75), w(0xdf), w(0x6e), w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89), \
+        w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b), w(0xfc), w(0x56), w(0x3e), w(0x4b), \
+        w(0xc6), w(0xd2), w(0x79), w(0x20), w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4), \
+        w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31), w(0xb1), w(0x12), w(0x10), w(0x59), \
+        w(0x27), w(0x80), w(0xec), w(0x5f), w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d), \
+        w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef), w(0xa0), w(0xe0), w(0x3b), w(0x4d), \
+        w(0xae), w(0x2a), w(0xf5), w(0xb0), w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61), \
+        w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26), w(0xe1), w(0x69), w(0x14), w(0x63), \
+        w(0x55), w(0x21), w(0x0c), w(0x7d)                                                                          \
   }
 
-#define mm_data(w)                                                          \
-  { /* basic data for forming finite field tables */                        \
-    w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07), \
-        w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e),      \
-        w(0x0f), w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15),      \
-        w(0x16), w(0x17), w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c),      \
-        w(0x1d), w(0x1e), w(0x1f), w(0x20), w(0x21), w(0x22), w(0x23),      \
-        w(0x24), w(0x25), w(0x26), w(0x27), w(0x28), w(0x29), w(0x2a),      \
-        w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f), w(0x30), w(0x31),      \
-        w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37), w(0x38),      \
-        w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),      \
-        w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46),      \
-        w(0x47), w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d),      \
-        w(0x4e), w(0x4f), w(0x50), w(0x51), w(0x52), w(0x53), w(0x54),      \
-        w(0x55), w(0x56), w(0x57), w(0x58), w(0x59), w(0x5a), w(0x5b),      \
-        w(0x5c), w(0x5d), w(0x5e), w(0x5f), w(0x60), w(0x61), w(0x62),      \
-        w(0x63), w(0x64), w(0x65), w(0x66), w(0x67), w(0x68), w(0x69),      \
-        w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f), w(0x70),      \
-        w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),      \
-        w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e),      \
-        w(0x7f), w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85),      \
-        w(0x86), w(0x87), w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c),      \
-        w(0x8d), w(0x8e), w(0x8f), w(0x90), w(0x91), w(0x92), w(0x93),      \
-        w(0x94), w(0x95), w(0x96), w(0x97), w(0x98), w(0x99), w(0x9a),      \
-        w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f), w(0xa0), w(0xa1),      \
-        w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7), w(0xa8),      \
-        w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),      \
-        w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6),      \
-        w(0xb7), w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd),      \
-        w(0xbe), w(0xbf), w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4),      \
-        w(0xc5), w(0xc6), w(0xc7), w(0xc8), w(0xc9), w(0xca), w(0xcb),      \
-        w(0xcc), w(0xcd), w(0xce), w(0xcf), w(0xd0), w(0xd1), w(0xd2),      \
-        w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7), w(0xd8), w(0xd9),      \
-        w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf), w(0xe0),      \
-        w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),      \
-        w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee),      \
-        w(0xef), w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5),      \
-        w(0xf6), w(0xf7), w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc),      \
-        w(0xfd), w(0xfe), w(0xff)                                           \
+#define mm_data(w)                                                                                                  \
+  { /* basic data for forming finite field tables */                                                                \
+    w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07), w(0x08), w(0x09), w(0x0a), w(0x0b),     \
+        w(0x0c), w(0x0d), w(0x0e), w(0x0f), w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17), \
+        w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f), w(0x20), w(0x21), w(0x22), w(0x23), \
+        w(0x24), w(0x25), w(0x26), w(0x27), w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f), \
+        w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37), w(0x38), w(0x39), w(0x3a), w(0x3b), \
+        w(0x3c), w(0x3d), w(0x3e), w(0x3f), w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47), \
+        w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f), w(0x50), w(0x51), w(0x52), w(0x53), \
+        w(0x54), w(0x55), w(0x56), w(0x57), w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f), \
+        w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67), w(0x68), w(0x69), w(0x6a), w(0x6b), \
+        w(0x6c), w(0x6d), w(0x6e), w(0x6f), w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77), \
+        w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f), w(0x80), w(0x81), w(0x82), w(0x83), \
+        w(0x84), w(0x85), w(0x86), w(0x87), w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f), \
+        w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97), w(0x98), w(0x99), w(0x9a), w(0x9b), \
+        w(0x9c), w(0x9d), w(0x9e), w(0x9f), w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7), \
+        w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf), w(0xb0), w(0xb1), w(0xb2), w(0xb3), \
+        w(0xb4), w(0xb5), w(0xb6), w(0xb7), w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf), \
+        w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7), w(0xc8), w(0xc9), w(0xca), w(0xcb), \
+        w(0xcc), w(0xcd), w(0xce), w(0xcf), w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7), \
+        w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf), w(0xe0), w(0xe1), w(0xe2), w(0xe3), \
+        w(0xe4), w(0xe5), w(0xe6), w(0xe7), w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef), \
+        w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7), w(0xf8), w(0xf9), w(0xfa), w(0xfb), \
+        w(0xfc), w(0xfd), w(0xfe), w(0xff)                                                                          \
   }
 
 static const uint_8t sbox[256] = sb_data(f1);
@@ -288,8 +240,7 @@
   w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4);
   return 0x63 ^ ((w ^ (w >> 8)) & 0xff);
 #else
-  return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) ^ (x >> 7) ^
-         (x >> 6) ^ (x >> 5) ^ (x >> 4);
+  return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) ^ (x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4);
 #endif
 }
 
@@ -519,12 +470,10 @@
   dt[2] = is_box(gfm_d(st[8]) ^ gfm_9(st[9]) ^ gfm_e(st[10]) ^ gfm_b(st[11]));
   dt[7] = is_box(gfm_b(st[8]) ^ gfm_d(st[9]) ^ gfm_9(st[10]) ^ gfm_e(st[11]));
 
-  dt[12] =
-      is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15]));
+  dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15]));
   dt[1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15]));
   dt[6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15]));
-  dt[11] =
-      is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15]));
+  dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15]));
 }
 
 #if defined(AES_ENC_PREKEYED) || defined(AES_DEC_PREKEYED)
@@ -536,8 +485,7 @@
     128, 192, 16, 24 and 32).
 */
 
-return_type aes_set_key(const unsigned char key[], length_type keylen,
-                        aes_context ctx[1]) {
+return_type aes_set_key(const unsigned char key[], length_type keylen, aes_context ctx[1]) {
   uint_8t cc, rc, hi;
 
   switch (keylen) {
@@ -595,8 +543,7 @@
 
 /*  Encrypt a single block of 16 bytes */
 
-return_type aes_encrypt(const unsigned char in[N_BLOCK],
-                        unsigned char out[N_BLOCK], const aes_context ctx[1]) {
+return_type aes_encrypt(const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1]) {
   if (ctx->rnd) {
     uint_8t s1[N_BLOCK], r;
     copy_and_key(s1, in, ctx->ksch);
@@ -623,9 +570,8 @@
 
 /* CBC encrypt a number of blocks (input and return an IV) */
 
-return_type aes_cbc_encrypt(const unsigned char* in, unsigned char* out,
-                            int n_block, unsigned char iv[N_BLOCK],
-                            const aes_context ctx[1]) {
+return_type aes_cbc_encrypt(
+    const unsigned char* in, unsigned char* out, int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1]) {
   while (n_block--) {
     xor_block(iv, in);
     if (aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
@@ -642,8 +588,7 @@
 
 /*  Decrypt a single block of 16 bytes */
 
-return_type aes_decrypt(const unsigned char in[N_BLOCK],
-                        unsigned char out[N_BLOCK], const aes_context ctx[1]) {
+return_type aes_decrypt(const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1]) {
   if (ctx->rnd) {
     uint_8t s1[N_BLOCK], r;
     copy_and_key(s1, in, ctx->ksch + ctx->rnd * N_BLOCK);
@@ -670,9 +615,8 @@
 
 /* CBC decrypt a number of blocks (input and return an IV) */
 
-return_type aes_cbc_decrypt(const unsigned char* in, unsigned char* out,
-                            int n_block, unsigned char iv[N_BLOCK],
-                            const aes_context ctx[1]) {
+return_type aes_cbc_decrypt(
+    const unsigned char* in, unsigned char* out, int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1]) {
   while (n_block--) {
     uint_8t tmp[N_BLOCK];
 
@@ -711,10 +655,11 @@
 
 /*  Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */
 
-void aes_encrypt_128(const unsigned char in[N_BLOCK],
-                     unsigned char out[N_BLOCK],
-                     const unsigned char key[N_BLOCK],
-                     unsigned char o_key[N_BLOCK]) {
+void aes_encrypt_128(
+    const unsigned char in[N_BLOCK],
+    unsigned char out[N_BLOCK],
+    const unsigned char key[N_BLOCK],
+    unsigned char o_key[N_BLOCK]) {
   uint_8t s1[N_BLOCK], r, rc = 1;
 
   if (o_key != key) block_copy(o_key, key);
@@ -765,10 +710,11 @@
 
 /*  Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */
 
-void aes_decrypt_128(const unsigned char in[N_BLOCK],
-                     unsigned char out[N_BLOCK],
-                     const unsigned char key[N_BLOCK],
-                     unsigned char o_key[N_BLOCK]) {
+void aes_decrypt_128(
+    const unsigned char in[N_BLOCK],
+    unsigned char out[N_BLOCK],
+    const unsigned char key[N_BLOCK],
+    unsigned char o_key[N_BLOCK]) {
   uint_8t s1[N_BLOCK], r, rc = 0x6c;
   if (o_key != key) block_copy(o_key, key);
 
@@ -831,10 +777,11 @@
 
 /*  Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */
 
-void aes_encrypt_256(const unsigned char in[N_BLOCK],
-                     unsigned char out[N_BLOCK],
-                     const unsigned char key[2 * N_BLOCK],
-                     unsigned char o_key[2 * N_BLOCK]) {
+void aes_encrypt_256(
+    const unsigned char in[N_BLOCK],
+    unsigned char out[N_BLOCK],
+    const unsigned char key[2 * N_BLOCK],
+    unsigned char o_key[2 * N_BLOCK]) {
   uint_8t s1[N_BLOCK], r, rc = 1;
   if (o_key != key) {
     block_copy(o_key, key);
@@ -909,10 +856,11 @@
 /*  Decrypt a single block of 16 bytes with 'on the fly'
     256 bit keying
 */
-void aes_decrypt_256(const unsigned char in[N_BLOCK],
-                     unsigned char out[N_BLOCK],
-                     const unsigned char key[2 * N_BLOCK],
-                     unsigned char o_key[2 * N_BLOCK]) {
+void aes_decrypt_256(
+    const unsigned char in[N_BLOCK],
+    unsigned char out[N_BLOCK],
+    const unsigned char key[2 * N_BLOCK],
+    unsigned char o_key[2 * N_BLOCK]) {
   uint_8t s1[N_BLOCK], r, rc = 0x80;
 
   if (o_key != key) {
diff --git a/gd/crypto_toolbox/aes.h b/gd/crypto_toolbox/aes.h
index 2ff6fbd..6d59743 100644
--- a/gd/crypto_toolbox/aes.h
+++ b/gd/crypto_toolbox/aes.h
@@ -80,28 +80,23 @@
 
 #if defined(AES_ENC_PREKEYED) || defined(AES_DEC_PREKEYED)
 
-return_type aes_set_key(const unsigned char key[], length_type keylen,
-                        aes_context ctx[1]);
+return_type aes_set_key(const unsigned char key[], length_type keylen, aes_context ctx[1]);
 #endif
 
 #if defined(AES_ENC_PREKEYED)
 
-return_type aes_encrypt(const unsigned char in[N_BLOCK],
-                        unsigned char out[N_BLOCK], const aes_context ctx[1]);
+return_type aes_encrypt(const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1]);
 
-return_type aes_cbc_encrypt(const unsigned char* in, unsigned char* out,
-                            int n_block, unsigned char iv[N_BLOCK],
-                            const aes_context ctx[1]);
+return_type aes_cbc_encrypt(
+    const unsigned char* in, unsigned char* out, int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1]);
 #endif
 
 #if defined(AES_DEC_PREKEYED)
 
-return_type aes_decrypt(const unsigned char in[N_BLOCK],
-                        unsigned char out[N_BLOCK], const aes_context ctx[1]);
+return_type aes_decrypt(const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1]);
 
-return_type aes_cbc_decrypt(const unsigned char* in, unsigned char* out,
-                            int n_block, unsigned char iv[N_BLOCK],
-                            const aes_context ctx[1]);
+return_type aes_cbc_decrypt(
+    const unsigned char* in, unsigned char* out, int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1]);
 #endif
 
 /*  The following calls are for 'on the fly' keying.  In this case the
@@ -125,30 +120,35 @@
 */
 
 #if defined(AES_ENC_128_OTFK)
-void aes_encrypt_128(const unsigned char in[N_BLOCK],
-                     unsigned char out[N_BLOCK],
-                     const unsigned char key[N_BLOCK], uint_8t o_key[N_BLOCK]);
+void aes_encrypt_128(
+    const unsigned char in[N_BLOCK],
+    unsigned char out[N_BLOCK],
+    const unsigned char key[N_BLOCK],
+    uint_8t o_key[N_BLOCK]);
 #endif
 
 #if defined(AES_DEC_128_OTFK)
-void aes_decrypt_128(const unsigned char in[N_BLOCK],
-                     unsigned char out[N_BLOCK],
-                     const unsigned char key[N_BLOCK],
-                     unsigned char o_key[N_BLOCK]);
+void aes_decrypt_128(
+    const unsigned char in[N_BLOCK],
+    unsigned char out[N_BLOCK],
+    const unsigned char key[N_BLOCK],
+    unsigned char o_key[N_BLOCK]);
 #endif
 
 #if defined(AES_ENC_256_OTFK)
-void aes_encrypt_256(const unsigned char in[N_BLOCK],
-                     unsigned char out[N_BLOCK],
-                     const unsigned char key[2 * N_BLOCK],
-                     unsigned char o_key[2 * N_BLOCK]);
+void aes_encrypt_256(
+    const unsigned char in[N_BLOCK],
+    unsigned char out[N_BLOCK],
+    const unsigned char key[2 * N_BLOCK],
+    unsigned char o_key[2 * N_BLOCK]);
 #endif
 
 #if defined(AES_DEC_256_OTFK)
-void aes_decrypt_256(const unsigned char in[N_BLOCK],
-                     unsigned char out[N_BLOCK],
-                     const unsigned char key[2 * N_BLOCK],
-                     unsigned char o_key[2 * N_BLOCK]);
+void aes_decrypt_256(
+    const unsigned char in[N_BLOCK],
+    unsigned char out[N_BLOCK],
+    const unsigned char key[2 * N_BLOCK],
+    unsigned char o_key[2 * N_BLOCK]);
 #endif
 
 #endif
diff --git a/gd/crypto_toolbox/crypto_toolbox.cc b/gd/crypto_toolbox/crypto_toolbox.cc
index 6582633..67f1a70 100644
--- a/gd/crypto_toolbox/crypto_toolbox.cc
+++ b/gd/crypto_toolbox/crypto_toolbox.cc
@@ -15,11 +15,13 @@
  */
 
 #include "crypto_toolbox/crypto_toolbox.h"
-#include "crypto_toolbox/aes.h"
 
 #include <endian.h>
+
 #include <algorithm>
 
+#include "crypto_toolbox/aes.h"
+
 namespace bluetooth {
 namespace crypto_toolbox {
 
@@ -48,8 +50,15 @@
 }
 
 /** helper for f5 */
-static Octet16 calculate_mac_key_or_ltk(const Octet16& t, uint8_t counter, uint8_t* key_id, const Octet16& n1,
-                                        const Octet16& n2, uint8_t* a1, uint8_t* a2, uint8_t* length) {
+static Octet16 calculate_mac_key_or_ltk(
+    const Octet16& t,
+    uint8_t counter,
+    uint8_t* key_id,
+    const Octet16& n1,
+    const Octet16& n2,
+    uint8_t* a1,
+    uint8_t* a2,
+    uint8_t* length) {
   constexpr size_t msg_len = 1 /* Counter size */ + 4 /* keyID size */ + OCTET16_LEN /* N1 size */ +
                              OCTET16_LEN /* N2 size */ + 7 /* A1 size*/ + 7 /* A2 size*/ + 2 /* Length size */;
 
@@ -87,8 +96,8 @@
   // DVLOG(2) << "ltk=" << HexEncode(ltk->data(), ltk->size());
 }
 
-Octet16 f6(const Octet16& w, const Octet16& n1, const Octet16& n2, const Octet16& r, uint8_t* iocap, uint8_t* a1,
-           uint8_t* a2) {
+Octet16
+f6(const Octet16& w, const Octet16& n1, const Octet16& n2, const Octet16& r, uint8_t* iocap, uint8_t* a1, uint8_t* a2) {
   const uint8_t msg_len = OCTET16_LEN /* N1 size */ + OCTET16_LEN /* N2 size */ + OCTET16_LEN /* R size */ +
                           3 /* IOcap size */ + 7 /* A1 size*/ + 7 /* A2 size*/;
 
@@ -130,8 +139,8 @@
 Octet16 ltk_to_link_key(const Octet16& ltk, bool use_h7) {
   Octet16 ilk; /* intermidiate link key */
   if (use_h7) {
-    constexpr Octet16 salt{0x31, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00,
-                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    constexpr Octet16 salt{
+        0x31, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
     ilk = h7(salt, ltk);
   } else {
     /* "tmp1" mapping to extended ASCII, little endian*/
@@ -147,8 +156,8 @@
 Octet16 link_key_to_ltk(const Octet16& link_key, bool use_h7) {
   Octet16 iltk; /* intermidiate long term key */
   if (use_h7) {
-    constexpr Octet16 salt{0x32, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00,
-                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    constexpr Octet16 salt{
+        0x32, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
     iltk = h7(salt, link_key);
   } else {
     /* "tmp2" mapping to extended ASCII, little endian */
@@ -161,14 +170,21 @@
   return h6(iltk, keyID_brle);
 }
 
-Octet16 c1(const Octet16& k, const Octet16& r, const uint8_t* pres, const uint8_t* preq, const uint8_t iat,
-           const uint8_t* ia, const uint8_t rat, const uint8_t* ra) {
+Octet16 c1(
+    const Octet16& k,
+    const Octet16& r,
+    const uint8_t* preq,
+    const uint8_t* pres,
+    const uint8_t iat,
+    const uint8_t* ia,
+    const uint8_t rat,
+    const uint8_t* ra) {
   Octet16 p1;
   auto it = p1.begin();
-  it = std::copy(pres, pres + 7, it);
-  it = std::copy(preq, preq + 7, it);
-  it = std::copy(&rat, &rat + 1, it);
   it = std::copy(&iat, &iat + 1, it);
+  it = std::copy(&rat, &rat + 1, it);
+  it = std::copy(preq, preq + 7, it);
+  it = std::copy(pres, pres + 7, it);
 
   for (uint8_t i = 0; i < OCTET16_LEN; i++) {
     p1[i] = r[i] ^ p1[i];
@@ -179,9 +195,9 @@
   std::array<uint8_t, 4> padding{0};
   Octet16 p2;
   it = p2.begin();
-  it = std::copy(padding.begin(), padding.end(), it);
-  it = std::copy(ia, ia + 6, it);
   it = std::copy(ra, ra + 6, it);
+  it = std::copy(ia, ia + 6, it);
+  it = std::copy(padding.begin(), padding.end(), it);
 
   for (uint8_t i = 0; i < OCTET16_LEN; i++) {
     p2[i] = p1bis[i] ^ p2[i];
diff --git a/gd/crypto_toolbox/crypto_toolbox.h b/gd/crypto_toolbox/crypto_toolbox.h
index 25f7f69..d507f73 100644
--- a/gd/crypto_toolbox/crypto_toolbox.h
+++ b/gd/crypto_toolbox/crypto_toolbox.h
@@ -24,17 +24,24 @@
 constexpr int OCTET16_LEN = 16;
 using Octet16 = std::array<uint8_t, OCTET16_LEN>;
 
-Octet16 c1(const Octet16& k, const Octet16& r, const uint8_t* pres, const uint8_t* preq, const uint8_t iat,
-           const uint8_t* ia, const uint8_t rat, const uint8_t* ra);
+Octet16 c1(
+    const Octet16& k,
+    const Octet16& r,
+    const uint8_t* pres,
+    const uint8_t* preq,
+    const uint8_t iat,
+    const uint8_t* ia,
+    const uint8_t rat,
+    const uint8_t* ra);
 Octet16 s1(const Octet16& k, const Octet16& r1, const Octet16& r2);
 
 extern Octet16 aes_128(const Octet16& key, const Octet16& message);
 extern Octet16 aes_cmac(const Octet16& key, const uint8_t* message, uint16_t length);
 extern Octet16 f4(uint8_t* u, uint8_t* v, const Octet16& x, uint8_t z);
-extern void f5(uint8_t* w, const Octet16& n1, const Octet16& n2, uint8_t* a1, uint8_t* a2, Octet16* mac_key,
-               Octet16* ltk);
-extern Octet16 f6(const Octet16& w, const Octet16& n1, const Octet16& n2, const Octet16& r, uint8_t* iocap, uint8_t* a1,
-                  uint8_t* a2);
+extern void f5(
+    uint8_t* w, const Octet16& n1, const Octet16& n2, uint8_t* a1, uint8_t* a2, Octet16* mac_key, Octet16* ltk);
+extern Octet16 f6(
+    const Octet16& w, const Octet16& n1, const Octet16& n2, const Octet16& r, uint8_t* iocap, uint8_t* a1, uint8_t* a2);
 extern Octet16 h6(const Octet16& w, std::array<uint8_t, 4> keyid);
 extern Octet16 h7(const Octet16& salt, const Octet16& w);
 extern uint32_t g2(uint8_t* u, uint8_t* v, const Octet16& x, const Octet16& y);
diff --git a/gd/crypto_toolbox/crypto_toolbox_test.cc b/gd/crypto_toolbox/crypto_toolbox_test.cc
index b5674c1..5c21a5d 100644
--- a/gd/crypto_toolbox/crypto_toolbox_test.cc
+++ b/gd/crypto_toolbox/crypto_toolbox_test.cc
@@ -16,13 +16,14 @@
  *
  ******************************************************************************/
 
-#include <gtest/gtest.h>
-
-#include "crypto_toolbox/aes.h"
 #include "crypto_toolbox/crypto_toolbox.h"
 
+#include <gtest/gtest.h>
+
 #include <vector>
 
+#include "crypto_toolbox/aes.h"
+
 namespace bluetooth {
 namespace crypto_toolbox {
 
@@ -32,8 +33,8 @@
 
   uint8_t m[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
-  uint8_t aes_cmac_k_m[] = {0x7d, 0xf7, 0x6b, 0x0c, 0x1a, 0xb8, 0x99, 0xb3,
-                            0x3e, 0x42, 0xf0, 0x47, 0xb9, 0x1b, 0x54, 0x6f};
+  uint8_t aes_cmac_k_m[] = {
+      0x7d, 0xf7, 0x6b, 0x0c, 0x1a, 0xb8, 0x99, 0xb3, 0x3e, 0x42, 0xf0, 0x47, 0xb9, 0x1b, 0x54, 0x6f};
 
   uint8_t output[16];
   aes_context ctx;
@@ -167,8 +168,8 @@
   std::array<uint8_t, 7> a2{0x00, 0xa7, 0x13, 0x70, 0x2d, 0xcf, 0xc1};
 
   Octet16 expected_ltk{0x69, 0x86, 0x79, 0x11, 0x69, 0xd7, 0xcd, 0x23, 0x98, 0x05, 0x22, 0xb5, 0x94, 0x75, 0x0a, 0x38};
-  Octet16 expected_mac_key{0x29, 0x65, 0xf1, 0x76, 0xa1, 0x08, 0x4a, 0x02,
-                           0xfd, 0x3f, 0x6a, 0x20, 0xce, 0x63, 0x6e, 0x20};
+  Octet16 expected_mac_key{
+      0x29, 0x65, 0xf1, 0x76, 0xa1, 0x08, 0x4a, 0x02, 0xfd, 0x3f, 0x6a, 0x20, 0xce, 0x63, 0x6e, 0x20};
 
   // algorithm expect all input to be in little endian format, so reverse
   std::reverse(std::begin(dhkey_w), std::end(dhkey_w));
@@ -197,8 +198,8 @@
 
   Octet16 MacKey{0x29, 0x65, 0xf1, 0x76, 0xa1, 0x08, 0x4a, 0x02, 0xfd, 0x3f, 0x6a, 0x20, 0xce, 0x63, 0x6e, 0x20};
 
-  Octet16 expected_aes_cmac{0xe3, 0xc4, 0x73, 0x98, 0x9c, 0xd0, 0xe8, 0xc5,
-                            0xd2, 0x6c, 0x0b, 0x09, 0xda, 0x95, 0x8f, 0x61};
+  Octet16 expected_aes_cmac{
+      0xe3, 0xc4, 0x73, 0x98, 0x9c, 0xd0, 0xe8, 0xc5, 0xd2, 0x6c, 0x0b, 0x09, 0xda, 0x95, 0x8f, 0x61};
 
   // algorithm expect all input to be in little endian format, so reverse
   std::reverse(std::begin(n1), std::end(n1));
@@ -244,8 +245,8 @@
 TEST(CryptoToolboxTest, bt_spec_example_d_6_test) {
   Octet16 key{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
   std::array<uint8_t, 4> keyID{0x6c, 0x65, 0x62, 0x72};
-  Octet16 expected_aes_cmac{0x2d, 0x9a, 0xe1, 0x02, 0xe7, 0x6d, 0xc9, 0x1c,
-                            0xe8, 0xd3, 0xa9, 0xe2, 0x80, 0xb1, 0x63, 0x99};
+  Octet16 expected_aes_cmac{
+      0x2d, 0x9a, 0xe1, 0x02, 0xe7, 0x6d, 0xc9, 0x1c, 0xe8, 0xd3, 0xa9, 0xe2, 0x80, 0xb1, 0x63, 0x99};
 
   // algorithm expect all input to be in little endian format, so reverse
   std::reverse(std::begin(key), std::end(key));
@@ -260,8 +261,8 @@
 TEST(CryptoToolboxTest, bt_spec_example_d_7_test) {
   Octet16 IRK{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
   Octet16 prand{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x81, 0x94};
-  Octet16 expected_aes_128{0x15, 0x9d, 0x5f, 0xb7, 0x2e, 0xbe, 0x23, 0x11,
-                           0xa4, 0x8c, 0x1b, 0xdc, 0xc4, 0x0d, 0xfb, 0xaa};
+  Octet16 expected_aes_128{
+      0x15, 0x9d, 0x5f, 0xb7, 0x2e, 0xbe, 0x23, 0x11, 0xa4, 0x8c, 0x1b, 0xdc, 0xc4, 0x0d, 0xfb, 0xaa};
   std::array<uint8_t, 3> expected_ah{0x0d, 0xfb, 0xaa};
 
   // algorithm expect all input to be in little endian format, so reverse
@@ -283,8 +284,8 @@
 TEST(CryptoToolboxTest, bt_spec_example_d_8_test) {
   Octet16 Key{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
   Octet16 SALT{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x6D, 0x70, 0x31};
-  Octet16 expected_aes_cmac{0xfb, 0x17, 0x35, 0x97, 0xc6, 0xa3, 0xc0, 0xec,
-                            0xd2, 0x99, 0x8c, 0x2a, 0x75, 0xa5, 0x70, 0x11};
+  Octet16 expected_aes_cmac{
+      0xfb, 0x17, 0x35, 0x97, 0xc6, 0xa3, 0xc0, 0xec, 0xd2, 0x99, 0x8c, 0x2a, 0x75, 0xa5, 0x70, 0x11};
 
   // algorithm expect all input to be in little endian format, so reverse
   std::reverse(std::begin(Key), std::end(Key));
@@ -300,8 +301,8 @@
 // BT Spec 5.0 | Vol 3, Part H D.9
 TEST(CryptoToolboxTest, bt_spec_example_d_9_test) {
   Octet16 LTK{0x36, 0x8d, 0xf9, 0xbc, 0xe3, 0x26, 0x4b, 0x58, 0xbd, 0x06, 0x6c, 0x33, 0x33, 0x4f, 0xbf, 0x64};
-  Octet16 expected_link_key{0x28, 0x7a, 0xd3, 0x79, 0xdc, 0xa4, 0x02, 0x53,
-                            0x0a, 0x39, 0xf1, 0xf4, 0x30, 0x47, 0xb8, 0x35};
+  Octet16 expected_link_key{
+      0x28, 0x7a, 0xd3, 0x79, 0xdc, 0xa4, 0x02, 0x53, 0x0a, 0x39, 0xf1, 0xf4, 0x30, 0x47, 0xb8, 0x35};
 
   // algorithm expect all input to be in little endian format, so reverse
   std::reverse(std::begin(LTK), std::end(LTK));
@@ -314,8 +315,8 @@
 // BT Spec 5.0 | Vol 3, Part H D.10
 TEST(CryptoToolboxTest, bt_spec_example_d_10_test) {
   Octet16 LTK{0x36, 0x8d, 0xf9, 0xbc, 0xe3, 0x26, 0x4b, 0x58, 0xbd, 0x06, 0x6c, 0x33, 0x33, 0x4f, 0xbf, 0x64};
-  Octet16 expected_link_key{0xbc, 0x1c, 0xa4, 0xef, 0x63, 0x3f, 0xc1, 0xbd,
-                            0x0d, 0x82, 0x30, 0xaf, 0xee, 0x38, 0x8f, 0xb0};
+  Octet16 expected_link_key{
+      0xbc, 0x1c, 0xa4, 0xef, 0x63, 0x3f, 0xc1, 0xbd, 0x0d, 0x82, 0x30, 0xaf, 0xee, 0x38, 0x8f, 0xb0};
 
   // algorithm expect all input to be in little endian format, so reverse
   std::reverse(std::begin(LTK), std::end(LTK));
diff --git a/gd/docs/architecture/architecture.md b/gd/docs/architecture/architecture.md
new file mode 100644
index 0000000..2a4ec8c
--- /dev/null
+++ b/gd/docs/architecture/architecture.md
@@ -0,0 +1,313 @@
+# Gabeldorsche Architecture
+
+[TOC]
+
+This document outlines some architectural considerations we've made when
+developing the Gabeldorsche (GD) Bluetooth stack.
+
+## Threading model
+
+First of all, the GD stack does not build on concepts of threads. Instead, it
+works with [`Handlers`](#handler). However, since GD ultimately runs on an OS,
+it still needs to interact with processes and threads before achieving the
+[`Handler`](#handler) abstraction.
+
+### Processes
+
+In general. three types of processes exist in the GD runtime environment:
+
+**Application processes**
+:   include third-party apps, other system components such as audio and telecom
+    services that interact with the _Bluetooth stack process_ APIs defined
+    through various RPC/IPC methods such as Binder, Socket IPC, gRPC, DBUS. and
+    so on, using languages such as AIDL or Protobuf. For Android applications,
+    although APIs are defined in AIDL, some boiler plate code is wrapped in Java
+    libraries exposed through code in
+    [`frameworks/base/core/java/android/bluetooth`](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/bluetooth/)
+    that is released to developers as
+    [Android SDK](https://developer.android.com/guide/topics/connectivity/bluetooth).
+
+**Hardware abstraction layer (HAL) processes**
+:   one or many processes from the vendor partition, and hence is hardware
+    depenedent. They interact with the _Bluetooth stack process_ via a set of
+    hardware abstraction APIs defined through RPC/IPC methods such as Binder,
+    Socket IPC, DBUS, and so on, using languages such as HIDL. On Android, this
+    would be HAL processes that implement HIDL APIs such as
+    [IBluetoothHci](https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/master/bluetooth/1.1/IBluetoothHci.hal)
+    and
+    [IBluetoothAudioProvider](https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/master/bluetooth/audio/2.0/IBluetoothAudioProvider.hal).
+
+**Bluetooth stack process**
+:   typically one single process that implements various Bluetooth protocols and
+    profiles above the Host Controller Interface (HCI) and below the Bluetooth
+    SDK APIs. On one hand, it servces the requests from _Application processes_;
+    on the other hand, it forwards these requests via interactions with _HAL
+    processes_. On Android, this process typically runs under AID_BLUETOOTH
+    (usually 1002) with process name "com.android.bluetooth". The process is
+    started in Java and loads native libraries through JNI. Other systems that
+    do not use Java virtual machine may have a pure native process. Multiple
+    threads may exist in this process for various reasons. The GD stack runs
+    entirely in this process.
+
+### Threads in Bluetooth stack process
+
+Currently, the goals of thread optimization in the Bluetooth stack are:
+
+*   Reduce the number of threads as much as possible to simplify synchronization
+*   Do blocking I/O operations in separate threads
+*   Try moving I/O operations into polling mode so that we can use event driven
+    methods to interact with it on main thread
+*   Move alarm and timer mechanisms to their calling threads to avoid a separate
+    alarm thread
+*   Isolate individual components so that each component can be started and
+    stopped individually without terminating the main thread
+*   Prefer data passing over data sharing among threads to reduce locking and
+    race conditions
+
+After above optimization, we are left with five main types of threads within the
+native code:
+
+**Main thread**
+:   The main workhorse in the Bluetooth stack. The thread's execution context is
+    further divided into [`Handlers`](#handler) that reside in individual
+    [`Modules`](#module). This thread can be divided further into smaller ones
+    if performance is constrained on the running platform. Deployer just needs
+    to bind handlers to different threads and that should not affect the overall
+    operation.
+
+**JNI thread**
+:   In the native thread, we treat the Java layer as a separate application as
+    its threading module is completely different. Therefore, we put a thread
+    between these two layers to buffer any blocking operation.
+
+**HCI thread (or other HW I/O thread)**
+:   This thread is responsible for deadling with hardware I/O and can be
+    potentially blocking. Hence it has its separate thread to avoid blocking the
+    main thread.
+
+**Audio worker thread**
+:   Responsible for audio encoding and decoding operations that require higher
+    precision execution timing. Such worker has its separate thread to avoid
+    being affected by the main thread.
+
+**Socket I/O thread**
+:   Communicate with various applications that uses the
+    [`BluetootSocket`](https://developer.android.com/reference/android/bluetooth/BluetoothSocket)
+    interface. It has its sepearate thread due to potential I/O delay.
+
+### Data flow diagram
+
+Function invocations between different components are abstracted as control
+packets (function closure) passed through queues. Data flow between components
+are data packets sent through queues, signaled using [`Reactor`](#reactor). They
+will merge to the input queue for each component. We define three types of
+queues:
+
+**Non-blocking queue**
+:   When users try to dequeue when it’s empty, or enqueue when it’s full, it
+    will return immediately. All queueing within a thread must be non-blocking,
+    because otherwise it will deadlock.
+
+**Blocking queue**
+:   When users try to dequeue when it’s empty, or enqueue when it’s full, it
+    will block, until other thread makes the queue to be writable/readable. It
+    can be used as a flow control mechanism to avoid too many packets from user
+    thread.
+
+**Leaky queue**
+:   Same as non-blocking queue, but it will flush when it’s full and user tries
+    to enqueue. This is useful for audio encoding.
+
+![Threading Model](./data_flow_diagram.png)
+
+## Building blocks
+
+### Module {#module}
+
+Code in GD is packed into C++ objects called
+[`Module`](https://android.googlesource.com/platform/system/bt/+/master/gd/module.h).
+A module standardized the following aspects of GD code:
+
+*   **Dependencies**: A module provides its own dependencies on other modules by
+    implementing `ListDependencies()`
+*   **Life Cycle**: A module must implement `Start()` and `Stop()` life cycle
+    methods
+*   **Threading Module**: The `Module` base class provides a `Handler` for code
+    execution context via `GetHandler()`
+*   **Metrics**: A `Module` can dump its state information for dumpsys through
+    `DumpState()`
+
+See its definition at: https://android.googlesource.com/platform/system/bt/+/master/gd/module.h
+
+### Handler {#handler}
+
+Similar to
+[`android.os.Handler`](https://developer.android.com/reference/android/os/Handler),
+[`bluetooth::os::Handler`](https://android.googlesource.com/platform/system/bt/+/master/gd/os/handler.h)
+provides a sequential execution context while hiding the concept of thread from
+the executing code.
+
+By scoping execution context into smaller areas, `Handler` benefits development
+in the following ways:
+
+*   Less need for locking due to sequential execution context
+*   Smaller context leads to easier management of code flow
+*   Separation from thread gives system deployer more freedom to tweak the
+    underlying thread allocation. For example, for real time OS without full
+    thread implementation, a `Handler` can be used to provide a near-thread
+    execution context
+
+Of course, there are downsides of using `Handler`, which developers should be
+cautious about:
+
+WARNING: Although multiple `Handler` could bind to the same thread, `Handler`
+does not gurantee sequential execution of code across different `Handler` even
+when the are on the same thread.
+
+WARNING: Locking among `Handlers` that were bound to the same thread may result
+in deadlock
+
+WARNING: Data must be copied between `Handler` to avoid both deadlock and race
+condition
+
+See its definition at: https://android.googlesource.com/platform/system/bt/+/master/gd/os/handler.h
+
+### Reactor {#reactor}
+
+[`bluetooth::os:Reactor`](https://android.googlesource.com/platform/system/bt/+/master/gd/os/reactor.h)
+implements the
+[Reactor Design Pattern](https://en.wikipedia.org/wiki/Reactor_pattern), in
+which concurrent _Events_ are demultiplexed by a _Synchronous Event
+Demultiplexer_ to a list of _Request Handlers_ registered through a
+_Dispatcher_.
+
+In a generic Linux operating system, such as Android, we implemented it using
+file descriptors such as
+[eventfd](http://man7.org/linux/man-pages/man2/eventfd.2.html) for `Handler`,
+[timerfd](http://man7.org/linux/man-pages/man2/timerfd_create.2.html) for
+`Alarm`, and [socketfd](http://man7.org/linux/man-pages/man2/socket.2.html) for
+data processing pipelines. In the context of file descriptors, events are
+catigorized into two types:
+
+*   **OnReadReady**: means that the demultiplexer has some events for the
+    handler and the handler can read at least one event from the underlying
+    event queue. This is often associated with `EPOLLIN`, `EPOLLHUP`,
+    `EPOLLRDHUP`, and `EPOLLERR`.
+*   **OnWriteReady**: means that the demultiplexer is ready to consume more
+    events from this handler, and the handler can write at least one event to
+    the underlying queue. this is often associated with `EPOLLOUT`.
+
+This pattern naturally creates a back pressure from one queue to another without
+any extra signaling mechanism. When used in networking stack like ours, it
+simplifies the signaling code flow.
+
+See its definition at:
+https://android.googlesource.com/platform/system/bt/+/master/gd/os/reactor.h
+
+A pure data use case of `Reactor` is a `Reactive Queue`, see its definition at:
+https://android.googlesource.com/platform/system/bt/+/master/gd/os/queue.h
+
+## Packet Definition Language (PDL)
+
+Packet parsing and serialization has been a big part of any networking stack. It
+is usually the first snippet of code that interface with a remote device. In the
+past, this has been achieved manually using macros like `STREAM_TO_UNIT8` or
+`UINT8_TO_STREAM`. This manual method is tedious and errorprone. To fix this, we
+created a Packet Definition Language that defines networking packet structure to
+the bits level. C++ headers and Python bindings will be automatically generated
+from its code generator and any fixes to the code generator will apply
+systematically to all packet code generated.
+
+Example PDL:
+
+```
+// Comments
+little_endian_packets // Whether this packet is big or small endian
+
+// Include header from other C++ header files
+custom_field SixBytes : 48 "packet/parser/test/" // expect six_bytes.h
+custom_field Variable "packet/parser/test/" // expect variable.h
+
+// A packet
+packet Parent {
+  _fixed_ = 0x12 : 8, // fixed field 0x12 that takes 8 bits
+  _size_(_payload_) : 8, // Size field that takes 8 bits
+  _payload_, // special payload field of variable size
+  footer : 8, // fiexed size footer of 8 bits
+}
+
+packet Child : Parent {
+  field_name : 16, // addition field append after Parent
+}
+
+// an enum of 4 bits
+enum FourBits : 4 {
+  ONE = 1,
+  TWO = 2,
+  THREE = 3,
+  FIVE = 5,
+  TEN = 10,
+  LAZY_ME = 15,
+}
+```
+
+See its documentation at:
+https://android.googlesource.com/platform/system/bt/+/master/gd/packet/parser/README
+
+## Calling convention between modules
+
+### Asynchronous server-client model
+
+For most communication among modules, developers should assume an asynchronous
+server-client model in a generic model like:
+
+```c++
+// Define callback function type
+using CallbackFunction = std::function<void(ParamType)>;
+
+// Asynchronous method definition
+bool Foo(Parameter param, CallbackFunction callback);
+
+// A new callback is passed for each asynchronous call
+// Always prefer lambda over std::bind
+CallbackFunction callback = [this] {
+  // do something
+};
+Parameter param = {
+  // something
+};
+if (Foo(param, callback)) {
+   // The callback will be invoked
+   // Callback must be invoked in the future
+} else {
+   // Failed, no need to wait
+}
+```
+
+Many protocols and profiles fit into such model such as `AclManager` and
+`L2cap`.
+
+### Synchronous database model
+
+In some cases, an asynchronous server-client model is not feasible. In this
+case, developers can consider a synchronous database model. In such a model,
+operations can happen synchronously with help of mutex. When the method returns,
+the changes must be reflected to all dependencies. Any changes in the internal
+states must be applied atomically.
+
+```c++
+// Synchronous method definition
+void Foo(Parameter param, Output* output);
+int Bar(Parameter param);
+Parameter param = {
+  // something
+};
+Output output = {};
+Foo(param, &output);
+// output can be used immediately
+int bar_output = Bar(param);
+// bar_output can be used immediately
+```
+
+Many storage and informational modules fit into this model such as `Metrics` and
+`Storage`.
diff --git a/gd/docs/architecture/data_flow_diagram.png b/gd/docs/architecture/data_flow_diagram.png
new file mode 100644
index 0000000..788b8ee
--- /dev/null
+++ b/gd/docs/architecture/data_flow_diagram.png
Binary files differ
diff --git a/gd/docs/architecture/style_guide.md b/gd/docs/architecture/style_guide.md
new file mode 100644
index 0000000..2e0ee53
--- /dev/null
+++ b/gd/docs/architecture/style_guide.md
@@ -0,0 +1,207 @@
+# Gabeldorsche Style Guide
+
+[TOC]
+
+## Base
+
+In general, when not mentioned in this document, developers should follow the
+Google C++ and Google Java style guide as much as possible.
+
+### Google C++ Style Guide
+
+C++ Style Guide: https://google.github.io/styleguide/cppguide.html
+
+### Android and Google Java Style Guide
+
+1.  Android Java Style Guide:
+    https://source.android.com/setup/contribute/code-style
+
+2.  when not covered by (1), see External Java Style Guide:
+    https://google.github.io/styleguide/javaguide.html
+
+line length limit is 120 characters for C++ and Java
+
+### Python Style Guide
+
+The GD stack uses the Google Python Style Guide:
+
+*   http://google.github.io/styleguide/pyguide.html
+
+with the following modifications as shown in the
+[.style.yapf](https://android.googlesource.com/platform/system/bt/+/refs/heads/master/.style.yapf) definition:
+
+```yapf
+based_on_style: google
+indent_width: 4
+column_limit: 120
+```
+
+## Build files
+
+*   One build target for the entire stack in system/bt (i.e. one cc_library())
+    *   If only part of the stack needs to be compiled, configure it using the
+        “target” configuration in Android.bp
+*   One build target for all unit tests (i.e. one cc_test)
+*   When needed, filgroup() can be created in Android.bp in sub-directories. The
+    main build target should use these filegroups() to build the main output
+    library.
+*   All targets must have host_supported == true unless it is dependent on the
+    OS
+*   If the stack needs to be compiled using other build system, then the build
+    files should also live in system/bt
+
+## Namespace and include
+
+*   Namespace must follow directory names
+*   Top level namespace for internal code is “bluetooth”
+*   Top level namespace for externally visible code is “android::bluetooth”
+*   Include path must be relative to the root directory of the stack. Normally
+    it is system/bt, for GD refactor code, it is system/bt/gd
+
+## Multiple implementations of the same header
+
+Since GD interact with many lower level components that are platform dependent,
+frequently there is need to implement the same header multiple times for
+different platform or hardware. When doing this:
+
+*   Avoid #define macros as much as possible. Instead put code into different
+    source files and selectively compile them for different targets.
+*   Convention of operating system used:
+    *   android/
+        *   All Android devices that use HIDL
+    *   linux/
+        *   All non-Android linux devices
+    *   linux_generic/
+        *   Android and non-Android linux devices
+
+## Directory structure
+
+Root directory under Android tree:
+[**system/bt/gd/**](https://android.googlesource.com/platform/system/bt/+/refs/heads/master/gd/)
+
+*   Directory structure should be as flat as possible
+*   Each file should contain at most one class
+*   Header, source code, and unit test should live in the same directory with
+    the following naming guideline:
+    *   Source: bb.cc
+    *   Header: bb.h
+    *   Test: bb_test.cc
+*   Each profile should have its own directory and module
+*   Source and sink, server and client profiles should live in two sub folders
+    of the same common directory where common code can be stored. However,
+    source and sink must have separate modules
+*   Module file is also the external API header
+*   Prefer underscore over dashes
+
+### Example: utility library with OS dependent implementation
+
+*   os/: OS dependent classes such as Alarm, Thread, Handler
+    *   Android.bp: Build file that defines file groups that would include
+        different source files based on compile time target
+    *   alarm.h: common header for alarm
+    *   linux_generic/: Implementations for generic Linux OS
+        *   alarm.cc: Linux generic implementation of alarm.h using timer_fd
+        *   alarm_test.cc: unit test for alarm.h
+    *   fuzz/: library needed for fuzz tests in the os/ library
+
+### Example: module with hardware dependent implementation
+
+*   hal/: Hardware abstraction layer such as HCI interfaces, Audio interfaces
+    *   Android.bp: Build file that defines file groups that would include
+        different source files based on compile time target
+    *   hci_hal.h: common library header
+    *   hci_hal_android_hidl.cc: implementation of hci_hal.h using Android HIDL
+    *   hci_hal_android_hidl_test.cc: unit tests for the Android HIDL
+        implementation
+    *   hci_hal_host_rootcanal.cc: implementation of hci_hal.h using root-canal
+        emulator
+    *   hci_hal_host_rootcanal_test.cc: unit tests for the root-canal emulator
+        implementation
+    *   facade.proto: gRPC automation interface definition for this layer
+    *   facade.h/cc: an implementation of the above gRPC interface for the GD
+        stack
+    *   cert/: certification tests for this module
+    *   fuzz/: library needed for fuzz tests in the hal/ module
+
+### Example: similar protocol with the same base
+
+*   l2cap/: L2CAP layer, splitted among classic and LE
+    *   classic/: Classic L2CAP module
+        *   cert/: certification tests for this module
+        *   internal/: internal code to be used only in classic
+        *   Source code and headers being exported to other modules
+    *   le/: LE L2CAP module
+        *   cert/: certification tests for this module
+        *   internal/: internal code to be used only in classic
+        *   Source code and headers being exported to other modules
+    *   internal/: L2CAP internal code that should not be used by sources
+        outside L2CAP
+        *   data_pipeline_manager.h
+        *   data_pipeline_manager.cc
+        *   data_pipeline_manager_mock.h: Mock of this class, used in unit tests
+        *   dynamic_channel_allocator.h
+        *   dynamic_channel_allocator.cc
+        *   dynamic_channel_allocator_test.cc: GTest unit test of this class
+        *   dynamic_channel_allocator_fuzz_test.cc: Fuzz test of this class
+    *   *.h/.cc: Common headers and sources that is exposed to other modules
+
+### Example: protocol or profiles with client and server side implementations
+
+*   a2dp/: A2DP profile
+    *   sink/: A2DP sink module (e.g. headset)
+    *   source/: A2DP source module (e.g. phone)
+*   avrcp/
+    *   controller/: AVRCP peripheral module (e.g. carkit)
+    *   target/: AVRCP target module (e.g. Phone)
+*   hfp/
+    *   hf/: Handsfree device (e.g. headset)
+    *   ag/: Audio gateway (e.g. phone)
+
+## External libraries
+
+To maintain high portability, we are trying to stick with C++ STL as much as
+possible. Hence, before including an external library, please ask the team for
+review.
+
+Examples of currently used libraries:
+
+*   boringssl: Google's openssl implementation
+*   small parts of libchrome, to be removed or replaced eventually
+    *   base::OnceCallback
+    *   base::Callback
+    *   base::BindOnce
+    *   base::Bind
+*   google-breakpad: host binary crash handler
+*   libbacktrace: print stacktrace on crash on host
+
+## Exposed symbols
+
+Given that entire Fluoride library is held in libbluetooth.so dynamic library
+file, we need a way to load this library and extract entry points to it. The
+only symbols that should be exposed are:
+
+*   An entry point to a normal running adapter module libbluetooth.so
+*   A header library to all exposed API service to profiles and layers
+*   An entry point to a certification interface, libbluetooth\_certification.so
+*   A header library to this certification stack
+
+## Logging
+
+Gabeldorsche uses `printf` style logging with macros defined in `os/log.h`. Five
+log levels are available.
+
+*   LOG_VERBOSE(fmt, args...): Will be disabled by default
+*   LOG_DEBUG(fmt, args...): Will be disabled by default
+*   LOG_INFO(fmt, args...): Enabled
+*   LOG_WARN(fmt, args...): Enabled
+*   LOG_ERROR(fmt, args...): Enabled
+*   LOG_ALWAYS_FATAL(fmt, args...): Enabled, will always crash
+*   ASSERT(condition): Enabled, will crash when condition is false
+*   ASSERT_LOG(conditon, fmt, args...): Enabled, will crash and print log when
+    condition is false
+
+In general, errors that are caused by remote device should never crash our stack
+and should be logged using LOG_WARN() only. Recoverable errors due to our stack
+or badly behaved bluetooth controller firmware should be logged using
+LOG_ERROR() before recovery. Non-recoverable errors should be logged as
+LOG_ALWAYS_FATAL() to crash the stack and restart.
diff --git a/gd/docs/testing/cert_test.md b/gd/docs/testing/cert_test.md
new file mode 100644
index 0000000..ef69681
--- /dev/null
+++ b/gd/docs/testing/cert_test.md
@@ -0,0 +1,254 @@
+# Gabeldorsche Certification Tests
+
+[TOC]
+
+## What is GD cert test
+
+A core problem behind Bluetooth interoperability testing is testers' inability to
+automate test operations on certain peripherals. For example, although an user
+can automate API calls on Android with tools like
+[SL4A](https://android.googlesource.com/platform/external/sl4a/) or ADB shell
+commands, they often struggle with automating corresponding operations on
+carkits, headsets, and smart watches. Even if they find a way to automatically
+control one model of such peripheral device using device maker's tools or
+self-developed after-market tools such as relays and robotic arms, such kind of
+tool usually does not scale to other models of peripherals due to their
+differences in physical user interfaces.
+
+GD certification test framework comes into rescue. In
+the framework, a common API is defined using a [gRPC](https://grpc.io/) protobuf
+for profiles such as HFP, AVRCP and function groups like pairing, scanning, and
+advertising. Everyone who wants to test their Bluetooth device implements an
+**gRPC Facade Server** using this protobuf definition. This server executable is
+responsible in gluing various gRPC based APIs with their device specific
+automation hook through a **Device Specific Driver**. The server will then
+expose an IP address and a port number to the test runner. A **Test Runner**
+process will load and execute the actual test case and use the auto-generated
+gRPC facade client APIs to interact with the device being tested. The following
+diagram shows how the system could be configured:
+
+![cert_test_architecture_drawing](./cert_test_architecture_drawing.png)
+
+## Terminology
+
+**gRPC Facade**
+:   A set of automation APIs of a specific profile (HFP, AVRCP, etc) or function
+    group (pairing, scanning, advertising, etc) defined in gRPC protobuf format.
+
+**Test Runner**
+:   A process that loads and runs the actual test cases and use auto-generated
+    gRPC facade client library to interact with test devices. Currently, the
+    preferred test runner is the [Android ACTS](https://android.googlesource.com/platform/tools/test/connectivity/+/refs/heads/master/acts/)
+    framework and test cases are written in Python for fast iteration and easy debugging
+
+**gRPC Facade Server**
+:   A concrete implementation of the gRPC automation APIs, which will convert
+    RPC requests into invocations of device specific automation hook using a
+    **Device Specific Driver**. This server can be written in any language and
+    on any platform. The server need to expose an IP address and 3 ports
+
+**Tester Signal Port**
+:   A port to indicate that the server is ready for operation to the **Test
+    Runner**
+
+**gRPC Root Server Port**
+:   A port that allows **Test Runner** to start and stop various functional
+    facade services
+
+**gRPC Facade Server Port**
+:   A port that allows **Test Runner** to interact with actual profile
+    implementations on the test device
+
+**Device Specific Driver**
+:   A library or some mechanism that allows the **gRPC Facade Server** to
+    control the test device. This library is opaque to the **Test Runner** that
+    should have no knowledge of how this component works * **Root-Canal**: A
+    Bluetooth PHY emulator that takes either LMP or HCI packets. The goal is for
+    this emulator to simulate a Physical RF environment without using any real
+    Bluetooth adatper.
+
+## How to run GD cert test in Android tree
+
+Assume user has an Android checkout and finished `source build/envsetup.sh` and
+`lunch` to a preferred target
+
+### Run GD cert tests on host machine
+
+```shell
+$ANDROID_BUILD_TOP/system/bt/gd/cert/run --host
+```
+
+#### Python 3.8+
+The cert tests require >python3.8 to operate and the associated python
+virtualenv package.  The script may help properly install these requisites.
+
+```shell
+source $ANDROID_BUILD_TOP/system/bt/gd/cert/set_up_virtualenv.sh
+```
+
+### Run GD cert tests on devices for the first time
+
+Connect at least two Android devices and follow on-screen instructions after
+running the following command
+
+```shell
+$ANDROID_BUILD_TOP/system/bt/gd/cert/set_up_and_run_device_cert.sh
+```
+
+### Run GD cert tests on devices for the second time and after
+
+Keeping the same set of devices connected
+
+```shell
+$ANDROID_BUILD_TOP/system/bt/gd/cert/run
+```
+
+### `system/bt/gd/cert/run` command reference
+
+*   `--host`: Run tests on host only using `root-canal`
+*   `--clean`: Remove any test setup files and do a clean test run
+*   `--repeat=N`: Repeat running the same set of tests N times without redoing
+    test setup
+*   `--test_file=<file_name>`: Running only tests listed in `<file_name>`
+*   `--test_filter=<test_filter>`: Test case filter in the format of
+    "TestClass:test_case_name", for multiple test cases, quote them using " and
+    separate each filter by space such as "TestClass1:test_case_1
+    TestClass2:test_case_2"
+
+### Run GD cert tests on devices over SSH
+
+The following assumptions assume the following configuration
+
+*   Local setup: Linux or MAC laptop with two Android phones connected
+*   Remote setup: Linux or MAC workstation
+
+1.  Check if your ADB version is up to date on both host machine and remote
+    workstation. Both versions should be 29.0.3 or above. If not, uninstall and
+    reinstall your adb.
+
+1.  Enable SSH port forwarding for both ADB port and various ports used by our
+    tests, run the following command on your Mac, assuming the following
+    configuration {value=2}
+
+    *   ADB Port: 5037
+    *   Cert Device:
+        *   gRPC Root Server Port: 8896
+        *   gRPC Facade Port: 8898
+        *   Signal Port: 8894
+    *   Device Under Test:
+        *   gRPC Root Server Port: 8897
+        *   gRPC Facade Port: 8899
+        *   Signal Port: 8895
+
+    Among these ports, ADB Port needs to be forwarded from local machine to
+    listen on remote workstation so that its adb client can connect to local
+    machine's adb server (`ssh -R`). Signal Port needs to be forwarded from
+    remote workstation to listen on local machine so that local phone can
+    connect to Test Runner listening on this port during Facade Server bring up
+    (`ssh -L`). Both gRPC Root Server Port and gRPC Facade Port need to be
+    forwarded from local machine to listen on remote workstation so that Test
+    Runner can connect to these ports on the Facade Server (`ssh -R`).
+
+    Hence, the resulting `ssh` command is:
+
+    ```shell
+    ssh -R 5037:127.0.0.1:5037 -R 6401:127.0.0.1:6401 -R 6402:127.0.0.1:6402 \
+    -R 6403:127.0.0.1:6403 -L 8894:127.0.0.1:8894 -L 8895:127.0.0.1:8895 \
+    -R 8896:127.0.0.1:8896 -R 8897:127.0.0.1:8897 -R 8898:127.0.0.1:8898 \
+    -R 8899:127.0.0.1:8899 <host_name>
+    ```
+
+1.  Connect all your devices, open a different terminal on your Mac and run
+
+    ```shell
+    adb kill-server && adb devices
+    ```
+
+    You should see devices on your local machine shows up
+
+1.  SSH into your remote workstation and run
+
+    ```shell
+    adb kill-server && adb devices
+    ```
+
+    You should see same set of devices connected to your local machine
+
+1.  Continue with device setup
+
+    ```shell
+    $ANDROID_BUILD_TOP/system/bt/gd/cert/set_up_and_run_device_cert.sh
+    ```
+
+1.  In subsequent runs
+
+    ```shell
+    $ANDROID_BUILD_TOP/system/bt/gd/cert/run
+    ```
+
+## How to debug GD cert tests
+
+Logs are produced and saved whenever GD cert tests runs. Depending on how the
+tests were run, the log root is different
+
+When running tests on local Android checkout, logs or most recent run are stored
+at
+
+*   /tmp/logs/HostOnlyCert/latest
+
+Navigate test logs
+
+In test root, the following logs are available:
+
+*   In every directory layer:
+    *   test_run_debug.txt: DEBUG level output from Python logging API
+        scoped at their respective layer based on the directory
+*   In test root directory:
+    *   test_summary.json: A summary test results, including stack traces
+        for any failures and metadata
+    *   test_configs.json: The ACTs config used to run this test
+    *   GdDevice_cert_stack_backing_process_coverage_summary.txt: code
+        coverage summary from llvm-cov
+*   In test class directory:
+    *   rootcanal_logs.txt: Root-Canal stdout and stderrr, host test only
+    *   Cert stack logs:
+        *   GdDevice_cert_stack_backing_logs.txt: facade server process log
+        *   cert_stack_btsnoop_hci.log: HCI packet log on cert device
+        *   cert_stack_system_log: Android phone logcat output, device based
+            test only
+    *   Device under test logs:
+        *   GdDevice_stack_under_test_backing_logs.txt: facade server
+            process log
+        *   stack_under_test_btsnoop_hci.log: HCI packet log on cert device
+        *   stack_under_test_system_log: Android phone logcat output, device
+            based test only
+    *   Individual test directories: logs for individual test cases
+
+## PTS test case coverage
+
+A Python decorator is used to indicate a test's association with a Bluetooth SIG
+Profile Tuning Suite (PTS) Qualification test. For example
+
+```python
+@metadata(
+    pts_test_id="L2CAP/EXF/BV-01-C",
+    pts_test_name="Extended Features Information Response for "
+    "Enhanced Retransmission Mode")
+```
+
+These information can be found at the Test Case Reference List (TCRL) document
+in
+[Qualification Test Requirements](https://www.bluetooth.com/specifications/qualification-test-requirements/)
+page at the Bluetooth SIG website.
+
+## Code coverage
+
+If the facade server binary is compiled using [`clang`](https://clang.llvm.org/)
+and with
+[`-fprofile-instr-generate -fcoverage-mapping`](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html)
+flags. A `.profraw` file will be generated in ech test class log directory after
+the test run. The Test Runner will then try to index these profiling data using
+[`llvm-profdata`](https://llvm.org/docs/CommandGuide/llvm-profdata.html),
+iteratively merge them from each test class, and generate a line-by-line test
+coverage report using
+[`llvm-cov`](https://llvm.org/docs/CommandGuide/llvm-cov.html).
diff --git a/gd/docs/testing/cert_test_architecture_drawing.png b/gd/docs/testing/cert_test_architecture_drawing.png
new file mode 100644
index 0000000..de49035
--- /dev/null
+++ b/gd/docs/testing/cert_test_architecture_drawing.png
Binary files differ
diff --git a/gd/docs/testing/gtest.md b/gd/docs/testing/gtest.md
new file mode 100644
index 0000000..b1aa779
--- /dev/null
+++ b/gd/docs/testing/gtest.md
@@ -0,0 +1,283 @@
+# GTest Unit Tests
+
+[TOC]
+
+## GTest Unit Tests
+
+[GTest](https://github.com/google/googletest) is a Google developed open source
+unit testing framework for C++ and C code. As the majority of GD code is writeen
+in C++, GTest provide the first layer of defence against bugs from the
+implementation level. Used in combination with
+[GMock](https://github.com/google/googlemock) developers can easily isolate
+classes and functions from their code to conduct unit testing.
+
+*   [GTest Primer](https://github.com/google/googletest/blob/master/googletest/docs/primer.md)
+*   [GMock for Dummies](https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md)
+
+### Test Binary
+
+All Gd unit test classes are compiled into a single binary
+[bluetooth_test_gd](https://android.googlesource.com/platform/system/bt/+/master/gd/Android.bp).
+
+### Test Sources Definitions
+
+*   Tests should live in the same directory as the source code
+*   Mocks should live in the same directory as the source header so that it can
+    be shared among multiple tests
+*   Tests should not modify global states that would affect other tests, so that
+    all tests could be executed using the same binary
+*   Each module can define a filegroup() that includes all test sources. This
+    filegroup is then included in a single cc_test() target that produce a
+    single test binary
+    [bluetooth_test_gd](https://android.googlesource.com/platform/system/bt/+/master/gd/Android.bp).
+    A single test binary simplifies the configuration effort needed for
+    compilation, presubmit and postsubmit execution, and so on.
+
+### How to run tests
+
+#### Use `atest`
+
+[ATest](https://source.android.com/compatibility/tests/development/atest) is an
+Android tool that allows a developers to run multiple modes of tests from the
+same `atest` command, including Java Instrumentation Tests, C/C++ GTests,
+CTS/GTS tests, etc. To use `atest` with GD, simplying sync your Android tree,
+run `source build/envsetup.sh` and `lunch` to a desired target. Then
+
+*   To run tests on device, the following command will automatically build,
+    push, and execute tests on a connected Android device
+
+    ```shell
+    atest bluetooth_test_gd
+    ```
+
+*   To run tests on host, the following command will automatically build and run
+    tests on your host machine
+
+    ```shell
+    atest --host bluetooth_test_gd
+    ```
+
+*   To run a single test case, use `<test_binary>:<test_class>#<test_method>`
+    format, such as
+
+    ```shell
+    atest --host bluetooth_test_gd:AclManagerTest#invoke_registered_callback_connection_complete_success
+    ```
+
+    See `atest --help` for more documentation on how to use atest to run various
+    tests
+
+#### Run it yourself (Not receommended unless really needed)
+
+Sometimes, you may want to execute the test binary directly because you want to
+attach a debugger or you want to avoid the test boostrap delay in `atest`. You
+can do it with the following steps
+
+1.  Sync Android tree, run `build/envsetup` and `lunch` desired target, `cd`
+    into Android checkout root directory
+
+1.  Make bluetooth_test_gd binary
+
+    ```shell
+    m -j40 bluetooth_test_gd
+    ```
+
+1.  Run the test on host {value=3}
+
+    ```shell
+    $ANDROID_HOST_OUT/nativetest64/bluetooth_test_gd/bluetooth_test_gd
+    ```
+
+1.  Run the test on device {value=4}
+
+    Push test to device
+
+    ```shell
+    adb push $ANDROID_PRODUCT_OUT/testcases/bluetooth_test_gd/arm64/bluetooth_test_gd /data/nativetest64/bluetooth_test_gd
+    ```
+
+    Run test using ADB
+
+    ```shell
+    adb shell /data/nativetest64/bluetooth_test_gd
+    ```
+
+1.  Run test with filter (Works the same way for device based test) {value=5}
+
+    ```shell
+    $ANDROID_HOST_OUT/nativetest64/bluetooth_test_gd/bluetooth_test_gd --gtest_filter=AclManagerTest.invoke_registered_callback_connection_complete_success*
+    ```
+
+    Note: the '*' wildcard is very important
+
+1.  Get command line help {value=6}
+
+    ```shell
+    $ANDROID_HOST_OUT/nativetest64/bluetooth_test_gd/bluetooth_test_gd --help
+    ```
+
+### Example: L2capClassicFixedChannelImplTest
+
+Note: All paths are relative to
+[system/bt/gd](https://android.googlesource.com/platform/system/bt/+/master/gd)
+
+#### Source code:
+
+*   [l2cap/classic/internal/fixed_channel_impl.h](https://android.googlesource.com/platform/system/bt/+/master/gd/l2cap/classic/internal/fixed_channel_impl.h)
+
+```c++
+#pragma once
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/fixed_channel.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/l2cap_packets.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+class Link;
+
+class FixedChannelImpl : public l2cap::internal::ChannelImpl {
+ public:
+  FixedChannelImpl(Cid cid, Link* link, os::Handler* l2cap_handler);
+  virtual ~FixedChannelImpl() = default;
+  hci::Address GetDevice() const;
+  virtual void RegisterOnCloseCallback(os::Handler* user_handler, FixedChannel::OnCloseCallback on_close_callback);
+  virtual void Acquire();
+  virtual void Release();
+  virtual bool IsAcquired() const;
+  virtual void OnClosed(hci::ErrorCode status);
+  virtual std::string ToString();
+  common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>* GetQueueUpEnd();
+  common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>* GetQueueDownEnd();
+  Cid GetCid() const;
+  Cid GetRemoteCid() const;
+ private:
+  // private fields omitted in doc ...
+};
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
+```
+
+*   [system/bt/gd/l2cap/classic/internal/fixed_channel_impl.cc](https://android.googlesource.com/platform/system/bt/+/master/gd/l2cap/classic/internal/fixed_channel_impl.cc)
+
+#### Mocks for dependencies' unit tests
+
+*   [l2cap/classic/internal/fixed_channel_impl_mock.h](https://android.googlesource.com/platform/system/bt/+/master/gd/l2cap/classic/internal/fixed_channel_impl_mock.h)
+
+```c++
+#pragma once
+
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace testing {
+
+class MockFixedChannelImpl : public FixedChannelImpl {
+ public:
+  MockFixedChannelImpl(Cid cid, Link* link, os::Handler* l2cap_handler) : FixedChannelImpl(cid, link, l2cap_handler) {}
+  MOCK_METHOD(void, RegisterOnCloseCallback,
+              (os::Handler * user_handler, FixedChannel::OnCloseCallback on_close_callback), (override));
+  MOCK_METHOD(void, Acquire, (), (override));
+  MOCK_METHOD(void, Release, (), (override));
+  MOCK_METHOD(bool, IsAcquired, (), (override, const));
+  MOCK_METHOD(void, OnClosed, (hci::ErrorCode status), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
+```
+
+#### Tests
+
+*   [l2cap/classic/internal/fixed_channel_impl_test.cc](https://android.googlesource.com/platform/system/bt/+/master/gd/l2cap/classic/internal/fixed_channel_impl_test.cc)
+
+```c++
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+
+#include "common/testing/bind_test_util.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+#include "os/handler.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+using l2cap::internal::testing::MockParameterProvider;
+using ::testing::_;
+using testing::MockLink;
+using ::testing::Return;
+
+class L2capClassicFixedChannelImplTest : public ::testing::Test {
+ public:
+  static void SyncHandler(os::Handler* handler) {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::seconds(1));
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    l2cap_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+};
+
+TEST_F(L2capClassicFixedChannelImplTest, get_device) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+  EXPECT_EQ(device.GetAddress(), fixed_channel_impl.GetDevice());
+}
+
+// Other test cases omitted in doc ...
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
+```
diff --git a/gd/dumpsys/Android.bp b/gd/dumpsys/Android.bp
new file mode 100644
index 0000000..5c87f70
--- /dev/null
+++ b/gd/dumpsys/Android.bp
@@ -0,0 +1,34 @@
+genrule {
+    name: "BluetoothFlatbufferTestData_h",
+    tools: [
+        "flatc",
+    ],
+    cmd: "$(location flatc) -I system/bt/gd -b --schema -o $(genDir) --cpp $(in) ",
+    srcs: [
+        "bluetooth_flatbuffer_test.fbs",
+    ],
+    out: [
+        "bluetooth_flatbuffer_test_generated.h", "bluetooth_flatbuffer_test.bfbs",
+    ],
+}
+
+cc_test {
+    name: "bluetooth_flatbuffer_test",
+    test_suites: ["device-tests"],
+    host_supported: true,
+    static_libs: [
+        "libgmock",
+        "libflatbuffers-cpp",
+    ],
+    srcs: [
+        "bluetooth_flatbuffer_test.cc",
+    ],
+    generated_headers: [
+        "BluetoothFlatbufferTestData_h",
+    ],
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
diff --git a/gd/dumpsys/bluetooth_flatbuffer_test.cc b/gd/dumpsys/bluetooth_flatbuffer_test.cc
new file mode 100644
index 0000000..4b83676
--- /dev/null
+++ b/gd/dumpsys/bluetooth_flatbuffer_test.cc
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "bluetooth_flatbuffer_test_generated.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace bluetooth {
+namespace dumpsys {
+
+class BluetoothFlatbufferTest : public ::testing::Test {
+ protected:
+  void SetUp() override {}
+
+  void TearDown() override {}
+};
+
+TEST_F(BluetoothFlatbufferTest, precondition) {}
+
+TEST_F(BluetoothFlatbufferTest, BuilderTest) {
+  flatbuffers::FlatBufferBuilder builder(1024);
+  auto string_private = builder.CreateString("String private");
+  auto string_opaque = builder.CreateString("String opaque");
+  auto string_anonymized = builder.CreateString("String anonymized");
+  auto string_any = builder.CreateString("String any");
+
+  TestTableBuilder table_builder(builder);
+  table_builder.add_string_private(string_private);
+  table_builder.add_string_opaque(string_opaque);
+  table_builder.add_string_anonymized(string_anonymized);
+  table_builder.add_string_any(string_any);
+
+  table_builder.add_int_private(123);
+  table_builder.add_int_opaque(456);
+  table_builder.add_int_anonymized(789);
+  table_builder.add_int_any(0xabc);
+
+  builder.Finish(table_builder.Finish());
+
+  const TestTable* test_table = GetTestTable(builder.GetBufferPointer());
+
+  ASSERT_EQ("String private", test_table->string_private()->str());
+  ASSERT_EQ("String opaque", test_table->string_opaque()->str());
+  ASSERT_EQ("String anonymized", test_table->string_anonymized()->str());
+  ASSERT_EQ("String any", test_table->string_any()->str());
+
+  ASSERT_EQ(123, test_table->int_private());
+  ASSERT_EQ(456, test_table->int_opaque());
+  ASSERT_EQ(789, test_table->int_anonymized());
+  ASSERT_EQ(0xabc, test_table->int_any());
+}
+
+}  // namespace dumpsys
+}  // namespace bluetooth
diff --git a/gd/dumpsys/bluetooth_flatbuffer_test.fbs b/gd/dumpsys/bluetooth_flatbuffer_test.fbs
new file mode 100644
index 0000000..f449813
--- /dev/null
+++ b/gd/dumpsys/bluetooth_flatbuffer_test.fbs
@@ -0,0 +1,17 @@
+// Bluetooth module test schema
+
+attribute "privacy";
+
+table TestTable {
+    string_private:string; // no privacy attribute implies private
+    string_opaque:string (privacy:"Opaque");
+    string_anonymized:string (privacy:"Anonymized");
+    string_any:string (privacy:"Any");
+
+    int_private:int32 (privacy:"Private"); // Explicitly private
+    int_opaque:int32 (privacy:"Opaque");
+    int_anonymized:int32 (privacy:"Anonymized");
+    int_any:int32 (privacy:"Any");
+}
+
+root_type TestTable;
diff --git a/gd/dumpsys/bundler/Android.bp b/gd/dumpsys/bundler/Android.bp
new file mode 100644
index 0000000..ea91c85
--- /dev/null
+++ b/gd/dumpsys/bundler/Android.bp
@@ -0,0 +1,73 @@
+filegroup {
+    name: "BluetoothFlatbufferBundlerSources",
+    srcs: [
+        "bundler.cc",
+        "main.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothFlatbufferBundlerTestSources",
+    srcs: [
+        "bundler.cc",
+        "test.cc",
+    ],
+}
+
+genrule {
+    name: "BluetoothGeneratedBundlerSchema_h_bfbs",
+    tools: [
+        "flatc",
+    ],
+    cmd: "$(location flatc) -I system/bt/gd -b --schema -o $(genDir) --cpp $(in) ",
+    srcs: [
+        "bundler.fbs",
+    ],
+    out: [
+        "bundler_generated.h", "bundler.bfbs",
+    ],
+}
+
+cc_defaults {
+    name: "bluetooth_flatbuffer_bundler_defaults",
+    cpp_std: "c++17",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedBundlerSchema_h_bfbs",
+    ],
+    sanitize: {
+        misc_undefined: ["bounds"],
+    },
+    static_libs: [
+        "libflatbuffers-cpp",
+    ],
+}
+
+cc_binary_host {
+    name: "bluetooth_flatbuffer_bundler",
+    srcs: [
+        ":BluetoothFlatbufferBundlerSources",
+    ],
+    defaults: [
+        "bluetooth_flatbuffer_bundler_defaults",
+    ],
+}
+
+cc_test {
+    name: "bluetooth_flatbuffer_bundler_test",
+    host_supported: true,
+    srcs: [
+        ":BluetoothFlatbufferBundlerTestSources",
+    ],
+    defaults: [
+        "bluetooth_flatbuffer_bundler_defaults",
+    ],
+    data: [
+        "test.bfbs",
+    ],
+}
diff --git a/gd/dumpsys/bundler/bundler.cc b/gd/dumpsys/bundler/bundler.cc
new file mode 100644
index 0000000..3a33730
--- /dev/null
+++ b/gd/dumpsys/bundler/bundler.cc
@@ -0,0 +1,303 @@
+/*
+ * 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.
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <cassert>
+#include <map>
+#include <vector>
+
+#include "bundler.h"
+#include "bundler_generated.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+using namespace bluetooth;
+using namespace dumpsys;
+
+struct Opts opts;
+
+/**
+ * Load a binary schema from persistent store using flatbuffer API.
+ *
+ * @param filename; Name of file to open and read.
+ * @param binary_schema: Backing store for flatbuffer binary schema data.
+ *
+ * @return: True if operation successful, false otherwise.
+ */
+bool LoadBinarySchema(const char* filename, std::string* binary_schema) {
+  assert(filename != nullptr);
+  assert(binary_schema != nullptr);
+  if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, binary_schema)) {
+    fprintf(stderr, "Unable to open binary flatbuffer schema file:%s\n", filename);
+    return false;
+  };
+  return true;
+}
+
+/**
+ * Verify a binary schema using flatbuffer API.
+ *
+ * @param schema: Raw binary schema to verify
+ *
+ * @return: True if operation successful, false otherwise.
+ */
+bool VerifyBinarySchema(const std::vector<const uint8_t>& raw_schema) {
+  flatbuffers::Verifier verifier(raw_schema.data(), raw_schema.size());
+  if (!reflection::VerifySchemaBuffer(verifier)) {
+    return false;
+  }
+  return true;
+}
+
+/**
+ * Bundle a set of binary flatbuffer schema into the bundler database.
+ *
+ * @param builder; Flatbuffer builder
+ * @param filenames: Set of filenames to include in bundle
+ * @param vector_map: Filename to filedata mapping
+ *
+ * @return: True if operation successful, false otherwise.
+ */
+bool CreateBinarySchemaBundle(
+    flatbuffers::FlatBufferBuilder* builder,
+    const std::vector<std::string>& filenames,
+    std::vector<flatbuffers::Offset<BundledSchemaMap>>* vector_map) {
+  assert(builder != nullptr);
+  assert(vector_map != nullptr);
+
+  for (auto filename : filenames) {
+    std::string string_schema;
+    if (!LoadBinarySchema(filename.c_str(), &string_schema)) {
+      fprintf(stderr, "Unable to load binary schema from filename:%s\n", filename.c_str());
+      return false;
+    }
+    std::vector<const uint8_t> raw_schema(string_schema.begin(), string_schema.end());
+    if (!VerifyBinarySchema(raw_schema)) {
+      fprintf(stderr, "Failed verification on binary schema filename:%s\n", filename.c_str());
+      return false;
+    }
+
+    const reflection::Schema* schema = reflection::GetSchema(raw_schema.data());
+    if (schema->root_table() == nullptr) {
+      fprintf(stderr, "Unable to find root table for binary flatbuffer schema:%s\n", filename.c_str());
+      return false;
+    }
+
+    auto name = builder->CreateString(schema->root_table()->name()->str());
+    auto data = builder->CreateVector<uint8_t>(raw_schema.data(), raw_schema.size());
+    vector_map->push_back(CreateBundledSchemaMap(*builder, name, data));
+
+    if (opts.verbose) {
+      fprintf(stdout, "Bundled binary schema file:%s\n", schema->root_table()->name()->c_str());
+    }
+  }
+  return true;
+}
+
+/**
+ * Write generated header file containing the bundled binary schema
+ * data and meta data
+ *
+ * @param data: Source file data.
+ * @param data_len: length of data
+ */
+void WriteHeaderFile(FILE* fp, const uint8_t* data, size_t data_len) {
+  assert(fp != nullptr);
+  std::string delim(kDefaultNamespaceDelim);
+  std::string ns_string(opts.ns_name);
+  std::vector<std::string> namespaces;
+
+  size_t start = 0;
+  size_t end = ns_string.find(delim);
+  while (end != std::string::npos) {
+    namespaces.push_back(ns_string.substr(start, end - start));
+    start = end + delim.size();
+    end = ns_string.find(delim, start);
+  }
+  if (start != 0 && start != std::string::npos) {
+    namespaces.push_back(ns_string.substr(start));
+  }
+
+  fprintf(
+      fp,
+      "// Generated file by bluetooth_flatbuffer bundler\n"
+      "#pragma once\n"
+      "#include <sys/types.h>\n"
+      "#include <string>\n");
+  for_each(
+      namespaces.begin(), namespaces.end(), [fp](const std::string& s) { fprintf(fp, "namespace %s {\n", s.c_str()); });
+  fprintf(
+      fp,
+      "extern const unsigned char* data;\n"
+      "extern const size_t data_size;\n"
+      "const std::string& GetBundledSchemaData();\n");
+  for_each(namespaces.crbegin(), namespaces.crend(), [fp](const std::string& s) {
+    fprintf(fp, "}  // namespace %s\n", s.c_str());
+  });
+  fprintf(
+      fp,
+      "namespace {\n"
+      "const unsigned char data_[] = {\n");
+
+  for (auto i = 0; i < data_len; i++) {
+    fprintf(fp, " 0x%02x", data[i]);
+    if (i != data_len - 1) {
+      fprintf(fp, ",");
+    }
+    if ((i + 1) % 16 == 0) {
+      fprintf(fp, "\n");
+    }
+  }
+  fprintf(fp, " };\n");
+  fprintf(
+      fp,
+      "const std::string string_data_(data_, data_ + sizeof(data_));\n"
+      "}  // namespace\n");
+  fprintf(fp, "const unsigned char* %s::data = data_;\n", opts.ns_name);
+  fprintf(fp, "const size_t %s::data_size = %zu;\n", opts.ns_name, data_len);
+  fprintf(fp, "const std::string& %s::GetBundledSchemaData() { return string_data_; }\n", opts.ns_name);
+}
+
+int ReadBundledSchema() {
+  const char* filename = opts.filename;
+  assert(filename != nullptr);
+
+  std::string flatfile_data;
+  if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, &flatfile_data)) {
+    fprintf(stderr, "Unable to load schema data file:%s\n", filename);
+    return -5;
+  }
+
+  auto bundle_schema = flatbuffers::GetRoot<BundledSchema>(flatfile_data.c_str());
+  const flatbuffers::Vector<flatbuffers::Offset<BundledSchemaMap>>* map = bundle_schema->map();
+
+  fprintf(stdout, "Bundle schema title:%s\n", bundle_schema->title()->c_str());
+  fprintf(stdout, "Bundle schema root_name:%s\n", bundle_schema->root_name()->c_str());
+  int cnt = 0;
+  for (auto it = map->cbegin(); it != map->cend(); ++it, cnt++) {
+    fprintf(stdout, "   %d name:%s schema:%s\n", cnt, it->name()->c_str(), "schema");
+  }
+  return EXIT_SUCCESS;
+}
+
+int WriteBundledSchema() {
+  const char* filename = opts.filename;
+  assert(filename != nullptr);
+
+  const char* main_root_name = opts.main_root_name;
+  if (main_root_name == nullptr) {
+    fprintf(stderr, "Must specify the name of the main root name for this bundle\n");
+    return EXIT_FAILURE;
+  }
+
+  std::vector<std::string> bfbs_filenames;
+  for (int i = 0; i < opts.arg.c; i++) {
+    bfbs_filenames.push_back(std::string(opts.arg.v[i]));
+  }
+  if (bfbs_filenames.empty()) {
+    fprintf(stderr, "No bfbs files are specified to bundle\n");
+    return EXIT_FAILURE;
+  }
+
+  flatbuffers::FlatBufferBuilder builder(1024);
+
+  std::vector<flatbuffers::Offset<BundledSchemaMap>> vector_map;
+  if (!CreateBinarySchemaBundle(&builder, bfbs_filenames, &vector_map)) {
+    fprintf(stderr, "Unable to bundle schema bfbs files\n");
+    return EXIT_FAILURE;
+  }
+
+  auto title = "Bundled schema tables";
+  auto schema_offset = CreateBundledSchemaDirect(builder, title, main_root_name, &vector_map);
+  builder.Finish(schema_offset);
+
+  std::string final_filename(opts.gen);
+  final_filename.append("/");
+  final_filename.append(filename);
+  if (!flatbuffers::SaveFile(
+          final_filename.c_str(), (const char*)builder.GetBufferPointer(), builder.GetSize(), helper::AsBinaryFile)) {
+    fprintf(stderr, "Unable to save file:%s\n", final_filename.c_str());
+    return EXIT_FAILURE;
+  }
+
+  std::string header(opts.gen);
+  header += ("/" + std::string(opts.filename) + ".h");
+  FILE* fp = fopen(header.c_str(), "w+");
+  if (fp == nullptr) {
+    fprintf(stdout, "Unable to open for writing header file:%s\n", header.c_str());
+    return EXIT_FAILURE;
+  }
+  WriteHeaderFile(fp, builder.GetBufferPointer(), builder.GetSize());
+  fclose(fp);
+  return EXIT_SUCCESS;
+}
+
+int Usage(int argc, char** argv) {
+  fprintf(
+      stderr,
+      "Usage: %s [-r | -w] [-f <filename>] [-g <gen_out_path>] [-n <namespace> ] [-v] -m <main_root_name> <file.bfbs "
+      "...>\n",
+      argv[0]);
+  fprintf(stderr, " -r|-w : Read or write a dumpsys file\n");
+  fprintf(stderr, " -f : Filename bundled schema to read or write (default:%s)\n", kDefaultBundleDataFile);
+  fprintf(stderr, " -g : Generated file output path\n");
+  fprintf(stderr, " -n : Namespace to embed binary output bundle data source\n");
+  fprintf(stderr, " -m : Name of the main root of this bundle\n");
+  fprintf(stderr, " -v : Verbose printing mode\n");
+  return EXIT_FAILURE;
+}
+
+void ParseArgs(int argc, char** argv) {
+  int opt;
+  int parsed_cnt = 1;
+  while ((opt = getopt(argc, argv, "f:g:m:n:rvw")) != -1) {
+    parsed_cnt++;
+    switch (opt) {
+      case 'f':
+        opts.filename = optarg;
+        parsed_cnt++;
+        break;
+      case 'g':
+        opts.gen = optarg;
+        parsed_cnt++;
+        break;
+      case 'm':
+        opts.main_root_name = optarg;
+        parsed_cnt++;
+        break;
+      case 'n':
+        opts.ns_name = optarg;
+        parsed_cnt++;
+        break;
+      case 'r':
+        opts.read = true;
+        break;
+      case 'w':
+        opts.write = true;
+        break;
+      case 'v':
+        opts.verbose = true;
+        break;
+      default:
+        exit(Usage(argc, argv));
+        break;
+    }
+  }
+  opts.arg.c = argc - parsed_cnt;
+  opts.arg.v = &argv[parsed_cnt];
+}
diff --git a/gd/dumpsys/bundler/bundler.fbs b/gd/dumpsys/bundler/bundler.fbs
new file mode 100644
index 0000000..a11fb7f
--- /dev/null
+++ b/gd/dumpsys/bundler/bundler.fbs
@@ -0,0 +1,19 @@
+// Bundled Schema
+//
+// Describes a collection of binary flatbuffer schema.
+//
+
+namespace bluetooth.dumpsys;
+
+table BundledSchemaMap {
+    name:string;
+    data:[ubyte];
+}
+
+table BundledSchema {
+    title:string;
+    root_name:string;
+    map:[BundledSchemaMap];
+}
+
+root_type BundledSchema;
diff --git a/gd/dumpsys/bundler/bundler.h b/gd/dumpsys/bundler/bundler.h
new file mode 100644
index 0000000..846bf5b
--- /dev/null
+++ b/gd/dumpsys/bundler/bundler.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+#pragma once
+
+namespace {
+constexpr char kDefaultBundleDataFile[] = "bundle_bfbs.bin";
+constexpr char kDefaultGeneratedOutputPath[] = ".";
+constexpr char kDefaultNamespace[] = "";
+constexpr char kDefaultNamespaceDelim[] = "::";
+}  // namespace
+
+struct Opts {
+  bool verbose{false};
+  bool read{false};
+  bool write{false};
+  const char* filename{kDefaultBundleDataFile};
+  const char* gen{kDefaultGeneratedOutputPath};
+  const char* main_root_name{nullptr};
+  const char* ns_name{kDefaultNamespace};
+  struct {
+    int c{0};
+    char** v{nullptr};
+  } arg;
+};
+extern Opts opts;
+
+namespace {
+namespace helper {  // Part of flatbuffers API
+constexpr bool AsBinaryFile = true;
+constexpr bool AsTextFile = false;
+}  // namespace helper
+
+}  // namespace
+
+/**
+ * Read and parse a previously generated bundle data file
+ *
+ **/
+int ReadBundledSchema();
+
+/**
+ * Generate a bundle data file from the binary flatbuffer schema
+ * files provided as input
+ *
+ **/
+int WriteBundledSchema();
+
+/**
+ * Print tool usage options
+ */
+int Usage(int argc, char** argv);
+
+/**
+ * Parse tool usage options
+ */
+void ParseArgs(int argc, char** argv);
diff --git a/gd/l2cap/security_policy.h b/gd/dumpsys/bundler/main.cc
similarity index 66%
copy from gd/l2cap/security_policy.h
copy to gd/dumpsys/bundler/main.cc
index 5a06401..23eb1e0 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/dumpsys/bundler/main.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,12 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
+#include "bundler.h"
+#include "bundler_generated.h"
 
-namespace bluetooth {
-namespace l2cap {
-
-class SecurityPolicy {};
-
-}  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+int main(int argc, char** argv) {
+  ParseArgs(argc, argv);
+  if (opts.read) {
+    exit(ReadBundledSchema());
+  }
+  if (opts.write) {
+    exit(WriteBundledSchema());
+  }
+}
diff --git a/gd/dumpsys/bundler/test.bfbs b/gd/dumpsys/bundler/test.bfbs
new file mode 100644
index 0000000..0dda6e5
--- /dev/null
+++ b/gd/dumpsys/bundler/test.bfbs
Binary files differ
diff --git a/gd/dumpsys/bundler/test.cc b/gd/dumpsys/bundler/test.cc
new file mode 100644
index 0000000..cd09de9
--- /dev/null
+++ b/gd/dumpsys/bundler/test.cc
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+#include <vector>
+
+#include "bundler.h"
+#include "bundler_generated.h"
+#include "flatbuffers/flatbuffers.h"
+
+bool LoadBinarySchema(const char* filename, std::string* binary_schema);
+bool VerifyBinarySchema(const std::vector<const uint8_t>& raw_schema);
+bool CreateBinarySchemaBundle(
+    flatbuffers::FlatBufferBuilder* builder,
+    const std::vector<std::string>& filenames,
+    std::vector<flatbuffers::Offset<bluetooth::dumpsys::BundledSchemaMap>>* vector_map);
+int WriteHeaderFile(FILE* fp, const uint8_t* data, size_t data_len);
+
+class BundlerTest : public ::testing::Test {
+ public:
+  void SetUp() override {}
+
+  void TearDown() override {}
+};
+
+TEST_F(BundlerTest, LoadBinarySchema) {
+  std::string string_schema;
+  ASSERT_FALSE(LoadBinarySchema(nullptr, &string_schema));
+  ASSERT_DEATH(LoadBinarySchema("system/bt/gd/dumpsys/bundler/test.bfbs", nullptr), "");
+  ASSERT_TRUE(LoadBinarySchema("system/bt/gd/dumpsys/bundler/test.bfbs", &string_schema));
+  ASSERT_FALSE(LoadBinarySchema("system/bt/gd/dumpsys/bundler/does_not_exist.bfbs", &string_schema));
+}
+
+TEST_F(BundlerTest, VerifyBinarySchema) {
+  std::string string_schema;
+  ASSERT_TRUE(LoadBinarySchema("system/bt/gd/dumpsys/bundler/test.bfbs", &string_schema));
+  std::vector<const uint8_t> raw_schema(string_schema.begin(), string_schema.end());
+  ASSERT_TRUE(VerifyBinarySchema(raw_schema));
+
+  std::vector<const uint8_t> bogus_raw_schema(string_schema.begin() + 1, string_schema.end());
+  ASSERT_FALSE(VerifyBinarySchema(bogus_raw_schema));
+}
+
+TEST_F(BundlerTest, CreateBinarySchemaBundle) {
+  flatbuffers::FlatBufferBuilder builder;
+  std::vector<std::string> filenames;
+  std::vector<flatbuffers::Offset<bluetooth::dumpsys::BundledSchemaMap>> vector_map;
+  ASSERT_TRUE(CreateBinarySchemaBundle(&builder, filenames, &vector_map));
+  ASSERT_EQ(0, vector_map.size());
+}
+
+TEST_F(BundlerTest, WriteHeaderFile) {
+  std::vector<uint8_t> data;
+  data.push_back(0x10);
+  data.push_back(0x11);
+  data.push_back(0x12);
+  data.push_back(0x13);
+  ASSERT_DEATH(WriteHeaderFile(nullptr, data.data(), data.size()), "");
+  FILE* fp = fopen("/tmp/test.h", "w+");
+  ASSERT_NE(fp, nullptr);
+  WriteHeaderFile(fp, data.data(), data.size());
+  fseek(fp, 0L, SEEK_SET);
+  char buf[16];
+  fread(buf, 1, 15, fp);
+  buf[12] = '\0';
+  std::string s(buf);
+  ASSERT_EQ("// Generated", s);
+  fclose(fp);
+  unlink("/tmp/test.h");
+}
diff --git a/gd/dumpsys_data.fbs b/gd/dumpsys_data.fbs
new file mode 100644
index 0000000..45ee176
--- /dev/null
+++ b/gd/dumpsys_data.fbs
@@ -0,0 +1,15 @@
+// Top level module dumpsys data schema
+include "module_unittest.fbs";
+include "shim/dumpsys.fbs";
+
+namespace bluetooth;
+
+attribute "privacy";
+
+table DumpsysData {
+    title:string;
+    shim_dumpsys_data:bluetooth.shim.DumpsysModuleData (privacy:"Any");
+    module_unittest_data:bluetooth.ModuleUnitTestData; // private
+}
+
+root_type DumpsysData;
diff --git a/gd/facade/facade_main.cc b/gd/facade/facade_main.cc
index 8ff1e72..002c566 100644
--- a/gd/facade/facade_main.cc
+++ b/gd/facade/facade_main.cc
@@ -22,31 +22,73 @@
 #include <unistd.h>
 #include <csignal>
 #include <cstring>
+#include <memory>
 #include <string>
 #include <thread>
 
+#include <client/linux/handler/exception_handler.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/backtrace_constants.h>
+
 #include "facade/grpc_root_server.h"
-#include "grpc/grpc_module.h"
-#include "hal/hci_hal.h"
 #include "hal/hci_hal_host_rootcanal.h"
 #include "hal/snoop_logger.h"
+#include "os/log.h"
 
-using ::bluetooth::hal::HciHalHostRootcanalConfig;
-using ::bluetooth::StackManager;
-using ::bluetooth::grpc::GrpcModule;
 using ::bluetooth::ModuleList;
+using ::bluetooth::StackManager;
+using ::bluetooth::hal::HciHalHostRootcanalConfig;
 using ::bluetooth::os::Thread;
 
 namespace {
 ::bluetooth::facade::GrpcRootServer grpc_root_server;
 
-void interrupt_handler(int) {
+struct sigaction old_act = {};
+void interrupt_handler(int signal_number) {
+  LOG_INFO("Stopping gRPC root server due to signal: %s[%d]", strsignal(signal_number), signal_number);
   grpc_root_server.StopServer();
+  if (old_act.sa_handler != nullptr) {
+    LOG_INFO("Calling saved signal handler");
+    old_act.sa_handler(signal_number);
+  }
 }
+struct sigaction new_act = {.sa_handler = interrupt_handler};
+
+bool crash_callback(const void* crash_context, size_t crash_context_size, void* context) {
+  pid_t tid = BACKTRACE_CURRENT_THREAD;
+  if (crash_context_size >= sizeof(google_breakpad::ExceptionHandler::CrashContext)) {
+    auto* ctx = static_cast<const google_breakpad::ExceptionHandler::CrashContext*>(crash_context);
+    tid = ctx->tid;
+    int signal_number = ctx->siginfo.si_signo;
+    LOG_ERROR("Process crashed, signal: %s[%d], tid: %d", strsignal(signal_number), signal_number, ctx->tid);
+  } else {
+    LOG_ERROR("Process crashed, signal: unknown, tid: unknown");
+  }
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+  if (backtrace == nullptr) {
+    LOG_ERROR("Failed to create backtrace object");
+    return false;
+  }
+  if (!backtrace->Unwind(0)) {
+    LOG_ERROR("backtrace->Unwind failed");
+    return false;
+  }
+  LOG_ERROR("Backtrace:");
+  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+    LOG_ERROR("%s", backtrace->FormatFrameData(i).c_str());
+  }
+  return true;
+}
+
 }  // namespace
 
 // The entry point for the binary with libbluetooth + facades
 int main(int argc, const char** argv) {
+  google_breakpad::MinidumpDescriptor descriptor(google_breakpad::MinidumpDescriptor::kMicrodumpOnConsole);
+  google_breakpad::ExceptionHandler eh(descriptor, nullptr, nullptr, nullptr, true, -1);
+  eh.set_crash_handler(crash_callback);
+
   int root_server_port = 8897;
   int grpc_port = 8899;
   int signal_port = 8895;
@@ -81,7 +123,7 @@
     }
   }
 
-  signal(SIGINT, interrupt_handler);
+  sigaction(SIGINT, &new_act, &old_act);
   grpc_root_server.StartServer("0.0.0.0", root_server_port, grpc_port);
   int tester_signal_socket = socket(AF_INET, SOCK_STREAM, 0);
   struct sockaddr_in addr;
diff --git a/gd/facade/grpc_root_server.cc b/gd/facade/grpc_root_server.cc
index e802dfe..1a9583c 100644
--- a/gd/facade/grpc_root_server.cc
+++ b/gd/facade/grpc_root_server.cc
@@ -22,11 +22,33 @@
 #include "facade/rootservice.grpc.pb.h"
 #include "grpc/grpc_module.h"
 #include "hal/facade.h"
-#include "hci/facade.h"
+#include "hci/facade/acl_manager_facade.h"
+#include "hci/facade/controller_facade.h"
+#include "hci/facade/facade.h"
+#include "hci/facade/le_acl_manager_facade.h"
+#include "hci/facade/le_advertising_manager_facade.h"
+#include "hci/facade/le_initiator_address_facade.h"
+#include "hci/facade/le_scanning_manager_facade.h"
+#include "hci/hci_layer.h"
+#include "hci/le_advertising_manager.h"
+#include "hci/le_scanning_manager.h"
 #include "l2cap/classic/facade.h"
+#include "l2cap/le/facade.h"
+#include "neighbor/connectability.h"
+#include "neighbor/discoverability.h"
+#include "neighbor/facade/facade.h"
+#include "neighbor/inquiry.h"
+#include "neighbor/name.h"
+#include "neighbor/page.h"
 #include "os/log.h"
 #include "os/thread.h"
+#include "security/facade.h"
+#include "security/security_module.h"
+#include "shim/dumpsys.h"
+#include "shim/facade/facade.h"
+#include "shim/l2cap.h"
 #include "stack_manager.h"
+#include "storage/storage_module.h"
 
 namespace bluetooth {
 namespace facade {
@@ -39,8 +61,10 @@
  public:
   RootFacadeService(int grpc_port) : grpc_port_(grpc_port) {}
 
-  ::grpc::Status StartStack(::grpc::ServerContext* context, const ::bluetooth::facade::StartStackRequest* request,
-                            ::bluetooth::facade::StartStackResponse* response) override {
+  ::grpc::Status StartStack(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::facade::StartStackRequest* request,
+      ::bluetooth::facade::StartStackResponse* response) override {
     if (is_running_) {
       return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is running");
     }
@@ -55,12 +79,43 @@
         break;
       case BluetoothModule::HCI:
         modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
-        modules.add<::bluetooth::hci::AclManagerFacadeModule>();
-        modules.add<::bluetooth::hci::ClassicSecurityManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::HciLayerFacadeModule>();
+        break;
+      case BluetoothModule::HCI_INTERFACES:
+        modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
+        modules.add<::bluetooth::hci::facade::HciLayerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::AclManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeAclManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeAdvertisingManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeInitiatorAddressFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeScanningManagerFacadeModule>();
+        modules.add<::bluetooth::neighbor::facade::NeighborFacadeModule>();
         break;
       case BluetoothModule::L2CAP:
+        modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeAdvertisingManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeInitiatorAddressFacadeModule>();
+        modules.add<::bluetooth::neighbor::facade::NeighborFacadeModule>();
         modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
         modules.add<::bluetooth::l2cap::classic::L2capClassicModuleFacadeModule>();
+        modules.add<::bluetooth::l2cap::le::L2capLeModuleFacadeModule>();
+        modules.add<::bluetooth::hci::facade::HciLayerFacadeModule>();
+        break;
+      case BluetoothModule::SECURITY:
+        modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
+        modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
+        modules.add<::bluetooth::security::SecurityModuleFacadeModule>();
+        modules.add<::bluetooth::neighbor::facade::NeighborFacadeModule>();
+        modules.add<::bluetooth::l2cap::classic::L2capClassicModuleFacadeModule>();
+        modules.add<::bluetooth::hci::facade::HciLayerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeAdvertisingManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeScanningManagerFacadeModule>();
+        break;
+      case BluetoothModule::SHIM:
+        modules.add<::bluetooth::shim::facade::ShimFacadeModule>();
+        modules.add<::bluetooth::shim::L2cap>();
         break;
       default:
         return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "invalid module under test");
@@ -78,8 +133,10 @@
     return ::grpc::Status::OK;
   }
 
-  ::grpc::Status StopStack(::grpc::ServerContext* context, const ::bluetooth::facade::StopStackRequest* request,
-                           ::bluetooth::facade::StopStackResponse* response) override {
+  ::grpc::Status StopStack(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::facade::StopStackRequest* request,
+      ::bluetooth::facade::StopStackResponse* response) override {
     if (!is_running_) {
       return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is not running");
     }
diff --git a/gd/facade/grpc_root_server.h b/gd/facade/grpc_root_server.h
index 9deba35..12faf26 100644
--- a/gd/facade/grpc_root_server.h
+++ b/gd/facade/grpc_root_server.h
@@ -16,11 +16,11 @@
 
 #pragma once
 
+#include <grpc++/grpc++.h>
+
 #include <memory>
 #include <string>
 
-#include <grpc++/grpc++.h>
-
 namespace bluetooth {
 namespace facade {
 
diff --git a/gd/facade/read_only_property_server.cc b/gd/facade/read_only_property_server.cc
index 3c57b87..fba3114 100644
--- a/gd/facade/read_only_property_server.cc
+++ b/gd/facade/read_only_property_server.cc
@@ -15,6 +15,7 @@
  */
 
 #include "facade/read_only_property_server.h"
+
 #include "hci/controller.h"
 
 namespace bluetooth {
@@ -23,8 +24,10 @@
 class ReadOnlyPropertyService : public ReadOnlyProperty::Service {
  public:
   ReadOnlyPropertyService(hci::Controller* controller) : controller_(controller) {}
-  ::grpc::Status ReadLocalAddress(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                  ::bluetooth::facade::BluetoothAddress* response) override {
+  ::grpc::Status ReadLocalAddress(
+      ::grpc::ServerContext* context,
+      const ::google::protobuf::Empty* request,
+      ::bluetooth::facade::BluetoothAddress* response) override {
     auto address = controller_->GetControllerMacAddress().ToString();
     response->set_address(address);
     return ::grpc::Status::OK;
diff --git a/gd/facade/read_only_property_server.h b/gd/facade/read_only_property_server.h
index dfac424..ac068b4 100644
--- a/gd/facade/read_only_property_server.h
+++ b/gd/facade/read_only_property_server.h
@@ -16,10 +16,10 @@
 
 #pragma once
 
-#include <memory>
-
 #include <grpc++/grpc++.h>
 
+#include <memory>
+
 #include "facade/rootservice.grpc.pb.h"
 #include "grpc/grpc_module.h"
 
diff --git a/gd/facade/rootservice.proto b/gd/facade/rootservice.proto
index eec30ad..6f2efcb 100644
--- a/gd/facade/rootservice.proto
+++ b/gd/facade/rootservice.proto
@@ -13,8 +13,10 @@
 enum BluetoothModule {
   HAL = 0;
   HCI = 1;
-  L2CAP = 2;
-  SECURITY = 3;
+  HCI_INTERFACES = 2;
+  L2CAP = 3;
+  SECURITY = 4;
+  SHIM = 5;
 }
 
 message StartStackRequest {
diff --git a/gd/fuzz/Android.bp b/gd/fuzz/Android.bp
new file mode 100644
index 0000000..801e2b9
--- /dev/null
+++ b/gd/fuzz/Android.bp
@@ -0,0 +1,6 @@
+filegroup {
+    name: "BluetoothFuzzHelperSources",
+    srcs: [
+        "helpers.cc",
+    ],
+}
diff --git a/gd/fuzz/helpers.cc b/gd/fuzz/helpers.cc
new file mode 100644
index 0000000..29e5305
--- /dev/null
+++ b/gd/fuzz/helpers.cc
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#include "fuzz/helpers.h"
+
+#include "common/bind.h"
+
+namespace bluetooth {
+namespace fuzz {
+
+// cribbed from https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#magic-separator
+std::vector<std::vector<uint8_t>> SplitInput(
+    const uint8_t* data, size_t size, const uint8_t* separator, size_t separatorSize) {
+  std::vector<std::vector<uint8_t>> result;
+  assert(separatorSize > 0);
+  auto beg = data;
+  auto end = data + size;
+  while (const uint8_t* pos = (const uint8_t*)memmem(beg, end - beg, separator, separatorSize)) {
+    result.push_back({beg, pos});
+    beg = pos + separatorSize;
+  }
+  if (beg < end) {
+    result.push_back({beg, end});
+  }
+  return result;
+}
+
+std::vector<uint8_t> GetArbitraryBytes(FuzzedDataProvider* fdp) {
+  return fdp->ConsumeBytes<uint8_t>(fdp->ConsumeIntegral<size_t>());
+}
+
+}  // namespace fuzz
+}  // namespace bluetooth
diff --git a/gd/fuzz/helpers.h b/gd/fuzz/helpers.h
new file mode 100644
index 0000000..c9cbd18
--- /dev/null
+++ b/gd/fuzz/helpers.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <cstdint>
+#include <vector>
+
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace fuzz {
+
+std::vector<std::vector<uint8_t>> SplitInput(
+    const uint8_t* data, size_t size, const uint8_t* separator, size_t separatorSize);
+
+std::vector<uint8_t> GetArbitraryBytes(FuzzedDataProvider* fdp);
+
+#define CONSTRUCT_VALID_UNIQUE_OTHERWISE_BAIL(T, name, data) \
+  auto name = std::make_unique<T>(T::FromBytes(data));       \
+  if (!name->IsValid()) {                                    \
+    return;                                                  \
+  }
+
+template <typename TView>
+void InvokeIfValid(common::ContextualOnceCallback<void(TView)> callback, std::vector<uint8_t> data) {
+  auto packet = TView::FromBytes(data);
+  if (!packet.IsValid()) {
+    return;
+  }
+  callback.InvokeIfNotEmpty(packet);
+}
+
+template <typename TView>
+void InvokeIfValid(common::ContextualCallback<void(TView)> callback, std::vector<uint8_t> data) {
+  auto packet = TView::FromBytes(data);
+  if (!packet.IsValid()) {
+    return;
+  }
+  callback.InvokeIfNotEmpty(packet);
+}
+
+}  // namespace fuzz
+}  // namespace bluetooth
diff --git a/gd/fuzz/run b/gd/fuzz/run
new file mode 100755
index 0000000..add645c
--- /dev/null
+++ b/gd/fuzz/run
@@ -0,0 +1,34 @@
+#! /bin/bash
+
+if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+  echo "ANDROID_BUILD_TOP is not set"
+fi
+
+if [[ -z "${ANDROID_HOST_OUT}" ]]; then
+  echo "ANDROID_HOST_OUT is not set for host run"
+fi
+
+HOST=false
+POSITIONAL=()
+while [[ $# -gt 0 ]]
+do
+key="$1"
+case $key in
+    --host)
+    HOST=true
+    shift # past argument
+    ;;
+    *)    # unknown option
+    POSITIONAL+=("$1") # save it in an array for later
+    shift # past argument
+    ;;
+esac
+done
+set -- "${POSITIONAL[@]}" # restore positional parameters
+
+TEST_NAME=bluetooth_gd_${1}_fuzz_test
+
+if [ "$HOST" == true ] ; then
+  HOST_ARCH=$($ANDROID_BUILD_TOP/build/soong/soong_ui.bash --dumpvar-mode HOST_ARCH)
+  SANITIZE_HOST=address $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="$(pwd)" $TEST_NAME && ${ANDROID_HOST_OUT}/fuzz/$HOST_ARCH/$TEST_NAME/$TEST_NAME $2
+fi
diff --git a/gd/grpc/async_grpc.h b/gd/grpc/async_grpc.h
deleted file mode 100644
index 7cde95d..0000000
--- a/gd/grpc/async_grpc.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <functional>
-#include <future>
-#include <memory>
-#include <mutex>
-
-#include <grpc++/grpc++.h>
-
-#include "os/log.h"
-
-namespace bluetooth {
-namespace grpc {
-
-// To be passed to gRPC async invocations as tag.
-// Function is called when the CompletionQueue.Next() returns this tag.
-// Then, user needs to delete this object.
-using GrpcAsyncEventCallback = std::function<void(bool)>;
-
-template <typename REQ, typename RES>
-class GrpcAsyncServerStreamingHandler {
- public:
-  virtual ~GrpcAsyncServerStreamingHandler() = default;
-
-  // Implementation for requesting the next specific type RPC, using provided parameters.
-  virtual void OnReadyForNextRequest(::grpc::ServerContext*, REQ* req, ::grpc::ServerAsyncWriter<RES>* res,
-                                     ::grpc::CompletionQueue* new_call_cq,
-                                     ::grpc::ServerCompletionQueue* notification_cq, void* tag) = 0;
-
-  virtual void OnRpcRequestReceived(REQ req) = 0;
-
-  virtual void OnRpcRequestFailed() {}
-
-  virtual void OnRpcFinished() {}
-
-  virtual void OnWriteSuccess() {}
-};
-
-// Provides API to upper layer users to control (request, write, finish) a server-streaming asynchronous RPC.
-// When each API is done, callback will be sent to the given GrpcAsyncServerStreamingHandler.
-// Each control box can take one active RPC at one time.
-
-// TODO: problems with this control box:
-//  1. RequestNewRpc is async, but Write and Stop is blocking users. Do we want to do this?
-//  2. Callback to user is done in the gRPC thread. Let's create a pool thread to give it to user?
-//  3. Currently it uses promise to synchronize between events. If we use os/handler it should be easier.
-template <typename REQ, typename RES>
-class GrpcAsyncServerStreamingControlBox {
- public:
-  GrpcAsyncServerStreamingControlBox(GrpcAsyncServerStreamingHandler<REQ, RES>* async_handler,
-                                     ::grpc::ServerCompletionQueue* cq)
-      : async_handler_(async_handler), cq_(cq) {}
-
-  void RequestNewRpc() {
-    ASSERT(my_state_ == MyState::IDLE);
-    context_ = std::make_unique<::grpc::ServerContext>();
-    req_ = std::make_unique<REQ>();
-    res_ = std::make_unique<::grpc::ServerAsyncWriter<RES>>(context_.get());
-    request_done_ = std::make_unique<GrpcAsyncEventCallback>([this](bool ok) { this->RequestDone(ok); });
-    async_handler_->OnReadyForNextRequest(context_.get(), req_.get(), res_.get(), cq_, cq_, request_done_.get());
-    my_state_ = MyState::REQUESTING;
-  }
-
-  void Write(const RES& res) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    if (my_state_ == MyState::IDLE || my_state_ == MyState::REQUESTING) {
-      LOG_INFO("stream already stopped");
-      return;
-    }
-    ASSERT(my_state_ == MyState::OPEN);
-    write_done_ = std::make_unique<GrpcAsyncEventCallback>([this](bool ok) { this->WriteDone(ok); });
-    my_state_ = MyState::WRITING;
-    res_->Write(res, write_done_.get());
-    promise_ = new std::promise<void>();
-    auto future = promise_->get_future();
-    future.wait();
-  }
-
-  void StopStreaming() {
-    std::unique_lock<std::mutex> lock(mutex_);
-    ASSERT(my_state_ == MyState::OPEN);
-    rpc_finish_ = std::make_unique<GrpcAsyncEventCallback>([this](bool ok) { this->RpcFinish(ok); });
-    my_state_ = MyState::FINISHING;
-    res_->Finish(::grpc::Status::OK, rpc_finish_.get());
-    promise_ = new std::promise<void>();
-    auto future = promise_->get_future();
-    future.wait();
-  }
-
- private:
-  void RequestDone(bool ok) {
-    ASSERT(my_state_ == MyState::REQUESTING);
-    if (ok) {
-      async_handler_->OnRpcRequestReceived(*req_);
-      my_state_ = MyState::OPEN;
-    } else {
-      clean_up();
-      async_handler_->OnRpcRequestFailed();
-      my_state_ = MyState::IDLE;
-    }
-  }
-
-  void WriteDone(bool ok) {
-    ASSERT(my_state_ == MyState::WRITING);
-    if (ok) {
-      my_state_ = MyState::OPEN;
-      async_handler_->OnWriteSuccess();
-    } else {
-      clean_up();
-      my_state_ = MyState::IDLE;
-      async_handler_->OnRpcFinished();
-    }
-    promise_->set_value();
-  }
-
-  void RpcFinish(bool ok) {
-    ASSERT(ok);
-    ASSERT(my_state_ == MyState::FINISHING);
-    clean_up();
-    my_state_ = MyState::IDLE;
-    async_handler_->OnRpcFinished();
-    promise_->set_value();
-  }
-
-  void clean_up() {
-    context_ = nullptr;
-    req_ = nullptr;
-    res_ = nullptr;
-  }
-
-  mutable std::mutex mutex_;
-  std::promise<void>* promise_ = nullptr;
-
-  GrpcAsyncServerStreamingHandler<REQ, RES>* async_handler_;
-  ::grpc::ServerCompletionQueue* cq_;
-
-  std::unique_ptr<::grpc::ServerContext> context_ = nullptr;
-  std::unique_ptr<REQ> req_ = nullptr;
-  std::unique_ptr<::grpc::ServerAsyncWriter<RES>> res_ = nullptr;
-
-  std::unique_ptr<GrpcAsyncEventCallback> request_done_ = nullptr;
-  std::unique_ptr<GrpcAsyncEventCallback> write_done_ = nullptr;
-  std::unique_ptr<GrpcAsyncEventCallback> rpc_finish_ = nullptr;
-
-  enum class MyState { IDLE, REQUESTING, OPEN, WRITING, FINISHING } my_state_ = MyState::IDLE;
-};
-
-}  // namespace grpc
-}  // namespace bluetooth
diff --git a/gd/grpc/grpc_module.cc b/gd/grpc/grpc_module.cc
index 07d7bb0b..d564287 100644
--- a/gd/grpc/grpc_module.cc
+++ b/gd/grpc/grpc_module.cc
@@ -17,7 +17,6 @@
 #include "grpc/grpc_module.h"
 
 #include "os/log.h"
-#include "grpc/async_grpc.h"
 
 using ::grpc::Server;
 using ::grpc::ServerBuilder;
@@ -25,8 +24,7 @@
 namespace bluetooth {
 namespace grpc {
 
-void GrpcModule::ListDependencies(ModuleList* list) {
-}
+void GrpcModule::ListDependencies(ModuleList* list) {}
 
 void GrpcModule::Start() {
   ASSERT(!started_);
@@ -53,7 +51,7 @@
   ASSERT(server_ != nullptr);
 
   for (const auto& facade : facades_) {
-    facade->OnServerStarted(completion_queue_.get());
+    facade->OnServerStarted();
   }
 }
 
@@ -97,8 +95,6 @@
       LOG_INFO("gRPC is shutdown");
       break;
     }
-    auto* data = static_cast<GrpcAsyncEventCallback*>(tag);
-    (*data)(ok);
   }
 }
 
@@ -106,10 +102,7 @@
   return "Grpc Module";
 }
 
-const ::bluetooth::ModuleFactory GrpcModule::Factory = ::bluetooth::ModuleFactory([]() {
-  return new GrpcModule();
-});
-
+const ::bluetooth::ModuleFactory GrpcModule::Factory = ::bluetooth::ModuleFactory([]() { return new GrpcModule(); });
 
 void GrpcFacadeModule::ListDependencies(ModuleList* list) {
   list->add<GrpcModule>();
diff --git a/gd/grpc/grpc_module.h b/gd/grpc/grpc_module.h
index 15d3126..77bf1b2 100644
--- a/gd/grpc/grpc_module.h
+++ b/gd/grpc/grpc_module.h
@@ -16,12 +16,12 @@
 
 #pragma once
 
-#include <functional>
-#include <vector>
-
 #include <grpc++/grpc++.h>
 #include <module.h>
 
+#include <functional>
+#include <vector>
+
 namespace bluetooth {
 namespace grpc {
 
@@ -59,7 +59,8 @@
 };
 
 class GrpcFacadeModule : public ::bluetooth::Module {
- friend GrpcModule;
+  friend GrpcModule;
+
  protected:
   void ListDependencies(ModuleList* list) override;
 
@@ -69,7 +70,7 @@
 
   virtual ::grpc::Service* GetService() const = 0;
 
-  virtual void OnServerStarted(::grpc::ServerCompletionQueue* cq) {}
+  virtual void OnServerStarted() {}
 
   virtual void OnServerStopped() {}
 
diff --git a/gd/hal/Android.bp b/gd/hal/Android.bp
index 4c9fa2a..8e370fd 100644
--- a/gd/hal/Android.bp
+++ b/gd/hal/Android.bp
@@ -41,8 +41,8 @@
 }
 
 filegroup {
-    name: "BluetoothCertSource_hci_hal",
+    name: "BluetoothHalFuzzSources",
     srcs: [
-        "cert/cert.cc",
+        "fuzz/fuzz_hci_hal.cc",
     ],
 }
diff --git a/gd/hal/cert/api.proto b/gd/hal/cert/api.proto
deleted file mode 100644
index 36a428d..0000000
--- a/gd/hal/cert/api.proto
+++ /dev/null
@@ -1,38 +0,0 @@
-syntax = "proto3";
-
-package bluetooth.hal.cert;
-
-import "google/protobuf/empty.proto";
-import "facade/common.proto";
-
-service HciHalCert {
-  rpc SendHciResetCommand(google.protobuf.Empty) returns (google.protobuf.Empty) {}
-  rpc SetScanMode(ScanModeSettings) returns (google.protobuf.Empty) {}
-  rpc SendHciCommand(HciCommandPacket) returns (google.protobuf.Empty) {}
-  rpc SendHciAcl(HciAclPacket) returns (google.protobuf.Empty) {}
-  rpc SendHciSco(HciScoPacket) returns (google.protobuf.Empty) {}
-
-  rpc FetchHciEvent(google.protobuf.Empty) returns (stream HciEventPacket) {}
-  rpc FetchHciAcl(google.protobuf.Empty) returns (stream HciAclPacket) {}
-  rpc FetchHciSco(google.protobuf.Empty) returns (stream HciScoPacket) {}
-}
-
-message ScanModeSettings {
-  uint32 mode = 1;
-}
-
-message HciEventPacket {
-  bytes payload = 1;
-}
-
-message HciCommandPacket {
-  bytes payload = 1;
-}
-
-message HciAclPacket {
-  bytes payload = 1;
-}
-
-message HciScoPacket {
-  bytes payload = 1;
-}
diff --git a/gd/hal/cert/cert.cc b/gd/hal/cert/cert.cc
deleted file mode 100644
index 09ea588..0000000
--- a/gd/hal/cert/cert.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "hal/cert/cert.h"
-
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-
-#include "grpc/grpc_event_queue.h"
-#include "hal/cert/api.grpc.pb.h"
-#include "hal/hci_hal.h"
-#include "hal/serialize_packet.h"
-#include "hci/hci_packets.h"
-
-namespace bluetooth {
-namespace hal {
-namespace cert {
-
-class HciHalCertService : public HciHalCert::Service, public ::bluetooth::hal::HciHalCallbacks {
- public:
-  explicit HciHalCertService(HciHal* hal) : hal_(hal) {
-    hal->registerIncomingPacketCallback(this);
-  }
-
-  ~HciHalCertService() override {
-    hal_->unregisterIncomingPacketCallback();
-  }
-
-  ::grpc::Status SendHciResetCommand(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                     ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(mutex_);
-    can_send_hci_command_ = false;
-    hal_->sendHciCommand(SerializePacket(hci::ResetBuilder::Create()));
-    std::this_thread::sleep_for(std::chrono::milliseconds(300));
-    while (!can_send_hci_command_) {
-      cv_.wait(lock);
-    }
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SetScanMode(::grpc::ServerContext* context, const ScanModeSettings* request,
-                             ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(mutex_);
-    can_send_hci_command_ = false;
-    unsigned int mode = request->mode();
-    hci::ScanEnable scan_enable;
-    switch (mode) {
-      case 0x00:
-        scan_enable = hci::ScanEnable::NO_SCANS;
-        break;
-      case 0x01:
-        scan_enable = hci::ScanEnable::INQUIRY_SCAN_ONLY;
-        break;
-      case 0x02:
-        scan_enable = hci::ScanEnable::PAGE_SCAN_ONLY;
-        break;
-      case 0x03:
-        scan_enable = hci::ScanEnable::INQUIRY_AND_PAGE_SCAN;
-        break;
-    }
-
-    hal_->sendHciCommand(SerializePacket(hci::WriteScanEnableBuilder::Create(scan_enable)));
-    while (!can_send_hci_command_) {
-      cv_.wait(lock);
-    }
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendHciCommand(::grpc::ServerContext* context, const ::bluetooth::hal::cert::HciCommandPacket* request,
-                                ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(mutex_);
-    can_send_hci_command_ = false;
-    std::string req_string = request->payload();
-    hal_->sendHciCommand(std::vector<uint8_t>(req_string.begin(), req_string.end()));
-    while (!can_send_hci_command_) {
-      cv_.wait(lock);
-    }
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendHciAcl(::grpc::ServerContext* context, const ::bluetooth::hal::cert::HciAclPacket* request,
-                            ::google::protobuf::Empty* response) override {
-    std::string req_string = request->payload();
-    hal_->sendAclData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendHciSco(::grpc::ServerContext* context, const ::bluetooth::hal::cert::HciScoPacket* request,
-                            ::google::protobuf::Empty* response) override {
-    std::string req_string = request->payload();
-    hal_->sendScoData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status FetchHciEvent(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                               ::grpc::ServerWriter<HciEventPacket>* writer) override {
-    return pending_hci_events_.RunLoop(context, writer);
-  };
-
-  ::grpc::Status FetchHciAcl(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                             ::grpc::ServerWriter<HciAclPacket>* writer) override {
-    return pending_acl_events_.RunLoop(context, writer);
-  };
-
-  ::grpc::Status FetchHciSco(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                             ::grpc::ServerWriter<HciScoPacket>* writer) override {
-    return pending_sco_events_.RunLoop(context, writer);
-  };
-
-  void hciEventReceived(bluetooth::hal::HciPacket event) override {
-    {
-      HciEventPacket response;
-      std::string response_str = std::string(event.begin(), event.end());
-      response.set_payload(response_str);
-      pending_hci_events_.OnIncomingEvent(std::move(response));
-    }
-    can_send_hci_command_ = true;
-    cv_.notify_one();
-  }
-
-  void aclDataReceived(bluetooth::hal::HciPacket data) override {
-    HciAclPacket response;
-    std::string response_str = std::string(data.begin(), data.end());
-    response.set_payload(response_str);
-    pending_acl_events_.OnIncomingEvent(std::move(response));
-  }
-
-  void scoDataReceived(bluetooth::hal::HciPacket data) override {
-    HciScoPacket response;
-    std::string response_str = std::string(data.begin(), data.end());
-    response.set_payload(response_str);
-    pending_sco_events_.OnIncomingEvent(std::move(response));
-  }
-
- private:
-  HciHal* hal_;
-  bool can_send_hci_command_ = true;
-  mutable std::mutex mutex_;
-  std::condition_variable cv_;
-  ::bluetooth::grpc::GrpcEventQueue<HciEventPacket> pending_hci_events_{"FetchHciEvent"};
-  ::bluetooth::grpc::GrpcEventQueue<HciAclPacket> pending_acl_events_{"FetchHciAcl"};
-  ::bluetooth::grpc::GrpcEventQueue<HciScoPacket> pending_sco_events_{"FetchHciSco"};
-};
-
-void HalCertModule::ListDependencies(ModuleList* list) {
-  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
-  list->add<HciHal>();
-}
-
-void HalCertModule::Start() {
-  ::bluetooth::grpc::GrpcFacadeModule::Start();
-  service_ = new HciHalCertService(GetDependency<HciHal>());
-}
-
-void HalCertModule::Stop() {
-  delete service_;
-  ::bluetooth::grpc::GrpcFacadeModule::Stop();
-}
-
-::grpc::Service* HalCertModule::GetService() const {
-  return service_;
-}
-
-const ModuleFactory HalCertModule::Factory = ::bluetooth::ModuleFactory([]() { return new HalCertModule(); });
-
-}  // namespace cert
-}  // namespace hal
-}  // namespace bluetooth
diff --git a/gd/hal/cert/simple_hal_test.py b/gd/hal/cert/simple_hal_test.py
index 4bb4fb1..d228f95 100644
--- a/gd/hal/cert/simple_hal_test.py
+++ b/gd/hal/cert/simple_hal_test.py
@@ -14,294 +14,316 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from __future__ import print_function
-
-from datetime import datetime, timedelta
-import logging
-import os
-import sys
-import time
-from queue import SimpleQueue, Empty
-
-sys.path.append(os.environ['ANDROID_BUILD_TOP'] + '/system/bt/gd')
+from datetime import timedelta
 
 from cert.gd_base_test import GdBaseTestClass
-from cert.event_callback_stream import EventCallbackStream
-from cert.event_asserts import EventAsserts
-from cert import rootservice_pb2 as cert_rootservice_pb2
-from facade import common_pb2
+from cert.event_stream import EventStream
+from cert.truth import assertThat
 from google.protobuf import empty_pb2
 from facade import rootservice_pb2 as facade_rootservice_pb2
-from hal.cert import api_pb2 as hal_cert_pb2
 from hal import facade_pb2 as hal_facade_pb2
+from bluetooth_packets_python3 import hci_packets
+import bluetooth_packets_python3 as bt_packets
 
 
 class SimpleHalTest(GdBaseTestClass):
 
+    def setup_class(self):
+        super().setup_class(dut_module='HAL', cert_module='HAL')
+
     def setup_test(self):
-        self.device_under_test = self.gd_devices[0]
-        self.cert_device = self.gd_cert_devices[0]
+        super().setup_test()
 
-        self.device_under_test.rootservice.StartStack(
-            facade_rootservice_pb2.StartStackRequest(
-                module_under_test=facade_rootservice_pb2.BluetoothModule.Value(
-                    'HAL'),
-            )
-        )
-        self.cert_device.rootservice.StartStack(
-            cert_rootservice_pb2.StartStackRequest(
-                module_to_test=cert_rootservice_pb2.BluetoothModule.Value(
-                    'HAL'),
-            )
-        )
+        self.send_dut_hci_command(hci_packets.ResetBuilder())
+        self.send_cert_hci_command(hci_packets.ResetBuilder())
 
-        self.device_under_test.wait_channel_ready()
-        self.cert_device.wait_channel_ready()
+    def send_cert_hci_command(self, command):
+        self.cert.hal.SendHciCommand(hal_facade_pb2.HciCommandPacket(payload=bytes(command.Serialize())))
 
-        self.device_under_test.hal.SendHciResetCommand(empty_pb2.Empty())
-        self.cert_device.hal.SendHciResetCommand(empty_pb2.Empty())
+    def send_cert_acl_data(self, handle, pb_flag, b_flag, acl):
+        lower = handle & 0xff
+        upper = (handle >> 8) & 0xf
+        upper = upper | int(pb_flag) & 0x3
+        upper = upper | ((int(b_flag) & 0x3) << 2)
+        lower_length = len(acl) & 0xff
+        upper_length = (len(acl) & 0xff00) >> 8
+        concatenated = bytes([lower, upper, lower_length, upper_length] + list(acl))
+        self.cert.hal.SendHciAcl(hal_facade_pb2.HciAclPacket(payload=concatenated))
 
-    def teardown_test(self):
-        self.device_under_test.rootservice.StopStack(
-            facade_rootservice_pb2.StopStackRequest()
-        )
-        self.cert_device.rootservice.StopStack(
-            cert_rootservice_pb2.StopStackRequest()
-        )
+    def send_dut_hci_command(self, command):
+        self.dut.hal.SendHciCommand(hal_facade_pb2.HciCommandPacket(payload=bytes(command.Serialize())))
+
+    def send_dut_acl_data(self, handle, pb_flag, b_flag, acl):
+        lower = handle & 0xff
+        upper = (handle >> 8) & 0xf
+        upper = upper | int(pb_flag) & 0x3
+        upper = upper | ((int(b_flag) & 0x3) << 2)
+        lower_length = len(acl) & 0xff
+        upper_length = (len(acl) & 0xff00) >> 8
+        concatenated = bytes([lower, upper, lower_length, upper_length] + list(acl))
+        self.dut.hal.SendHciAcl(hal_facade_pb2.HciAclPacket(payload=concatenated))
 
     def test_none_event(self):
-        with EventCallbackStream(self.device_under_test.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream:
-            hci_event_asserts = EventAsserts(hci_event_stream)
-            hci_event_asserts.assert_none(timeout=timedelta(seconds=1))
-
-    def test_example(self):
-        response = self.device_under_test.hal.SetLoopbackMode(
-            hal_facade_pb2.LoopbackModeSettings(enable=True)
-        )
+        with EventStream(self.dut.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream:
+            assertThat(hci_event_stream).emitsNone(timeout=timedelta(seconds=1))
 
     def test_fetch_hci_event(self):
-        self.device_under_test.hal.SetLoopbackMode(
-            hal_facade_pb2.LoopbackModeSettings(enable=True)
-        )
+        with EventStream(self.dut.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream:
 
-        with EventCallbackStream(self.device_under_test.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream:
-            hci_event_asserts = EventAsserts(hci_event_stream)
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x01\x04\x053\x8b\x9e0\x01'
-                )
-            )
-            hci_event_asserts.assert_event_occurs(
-                lambda packet: packet.payload == b'\x19\x08\x01\x04\x053\x8b\x9e0\x01')
+            self.send_dut_hci_command(
+                hci_packets.LeAddDeviceToConnectListBuilder(hci_packets.ConnectListAddressType.RANDOM,
+                                                            '0C:05:04:03:02:01'))
+            event = hci_packets.LeAddDeviceToConnectListCompleteBuilder(1, hci_packets.ErrorCode.SUCCESS)
+
+            assertThat(hci_event_stream).emits(lambda packet: bytes(event.Serialize()) in packet.payload)
+
+    def test_loopback_hci_command(self):
+        with EventStream(self.dut.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream:
+
+            self.send_dut_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.send_dut_hci_command(command)
+
+            assertThat(hci_event_stream).emits(lambda packet: bytes(command.Serialize()) in packet.payload)
 
     def test_inquiry_from_dut(self):
-        with EventCallbackStream(self.device_under_test.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream:
-            hci_event_asserts = EventAsserts(hci_event_stream)
-            self.cert_device.hal.SetScanMode(
-                hal_cert_pb2.ScanModeSettings(mode=3)
-            )
-            self.device_under_test.hal.SetInquiry(
-                hal_facade_pb2.InquirySettings(length=0x30, num_responses=0xff)
-            )
-            hci_event_asserts.assert_event_occurs(
-                lambda packet: b'\x02\x0f' in packet.payload
-                # Expecting an HCI Event (code 0x02, length 0x0f)
-            )
+        with EventStream(self.dut.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream:
+
+            self.send_cert_hci_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+            lap = hci_packets.Lap()
+            lap.lap = 0x33
+            self.send_dut_hci_command(hci_packets.InquiryBuilder(lap, 0x30, 0xff))
+
+            assertThat(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):
-        with EventCallbackStream(self.device_under_test.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream:
-            hci_event_asserts = EventAsserts(hci_event_stream)
+        with EventStream(self.dut.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream:
 
-            # Set the LE Address to 0D:05:04:03:02:01
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x05\x20\x06\x01\x02\x03\x04\x05\x0D'
-                )
-            )
-            # Set the LE Scan parameters (active, 40ms, 20ms, Random,
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x0B\x20\x07\x01\x40\x00\x20\x00\x01\x00'
-                )
-            )
-            # Enable Scanning (Disable duplicate filtering)
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x0C\x20\x02\x01\x00'
-                )
-            )
+            # DUT scans
+            self.send_dut_hci_command(hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
+            phy_scan_params = hci_packets.PhyScanParameters()
+            phy_scan_params.le_scan_interval = 6553
+            phy_scan_params.le_scan_window = 6553
+            phy_scan_params.le_scan_type = hci_packets.LeScanType.ACTIVE
 
-            # Set the LE Address to 0C:05:04:03:02:01
-            self.cert_device.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x05\x20\x06\x01\x02\x03\x04\x05\x0C'
-                )
-            )
-            # Set LE Advertising parameters
-            self.cert_device.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x06\x20\x0F\x00\x02\x00\x03\x00\x01\x00\xA1\xA2\xA3\xA4\xA5\xA6\x07\x00'
-                )
-            )
-            # Set LE Advertising data
-            self.cert_device.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x08\x20\x20\x0C\x0A\x09Im_A_Cert\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
-                )
-            )
-            # Enable Advertising
-            self.cert_device.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x0A\x20\x01\x01'
-                )
-            )
-            hci_event_asserts.assert_event_occurs(
-                lambda packet: b'Im_A_Cert' in packet.payload
-                # Expecting an HCI Event (code 0x3e, length 0x13, subevent 0x01 )
-            )
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedScanParametersBuilder(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                                                               hci_packets.LeScanningFilterPolicy.ACCEPT_ALL, 1,
+                                                               [phy_scan_params]))
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedScanEnableBuilder(hci_packets.Enable.ENABLED,
+                                                           hci_packets.FilterDuplicates.DISABLED, 0, 0))
+
+            # CERT Advertises
+            advertising_handle = 0
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    512,
+                    768,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    'A6:A5:A4:A3:A2:A1',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0x7F,
+                    0,  # SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ))
+
+            self.send_cert_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.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = advertising_handle
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
+
+            assertThat(hci_event_stream).emits(lambda packet: b'Im_A_Cert' in packet.payload)
+
             # Disable Advertising
-            self.cert_device.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x0A\x20\x01\x00'
-                )
-            )
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.DISABLED, [enabled_set]))
+
             # Disable Scanning
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x0C\x20\x02\x00\x00'
-                )
-            )
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedScanEnableBuilder(hci_packets.Enable.ENABLED,
+                                                           hci_packets.FilterDuplicates.DISABLED, 0, 0))
 
     def test_le_connection_dut_advertises(self):
-        with EventCallbackStream(self.device_under_test.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream, \
-                EventCallbackStream(self.cert_device.hal.FetchHciEvent(empty_pb2.Empty())) as cert_hci_event_stream, \
-                EventCallbackStream(self.device_under_test.hal.FetchHciAcl(empty_pb2.Empty())) as acl_data_stream, \
-                EventCallbackStream(self.cert_device.hal.FetchHciAcl(empty_pb2.Empty())) as cert_acl_data_stream:
+        with EventStream(self.dut.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream, \
+                EventStream(self.cert.hal.FetchHciEvent(empty_pb2.Empty())) as cert_hci_event_stream, \
+                EventStream(self.dut.hal.FetchHciAcl(empty_pb2.Empty())) as acl_data_stream, \
+                EventStream(self.cert.hal.FetchHciAcl(empty_pb2.Empty())) as cert_acl_data_stream:
 
-            hci_event_asserts = EventAsserts(hci_event_stream)
-            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
-            acl_data_asserts = EventAsserts(acl_data_stream)
-            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            # Cert Connects
+            self.send_cert_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.send_cert_hci_command(
+                hci_packets.LeExtendedCreateConnectionBuilder(hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS,
+                                                              hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                                                              hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                                                              '0D:05:04:03:02:01', 1, [phy_scan_params]))
 
-            # Set the CERT LE Address to 0C:05:04:03:02:01
-            self.cert_device.hal.SendHciCommand(
-                hal_cert_pb2.HciCommandPacket(
-                    payload=b'\x05\x20\x06\x01\x02\x03\x04\x05\x0C'
-                )
-            )
+            # DUT Advertises
+            advertising_handle = 0
+            self.send_dut_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
+                ))
 
-            # Direct connect to 0D:05:04:03:02:01
-            self.cert_device.hal.SendHciCommand(
-                hal_cert_pb2.HciCommandPacket(
-                    payload=b'\x0D\x20\x19\x11\x01\x22\x02\x00\x01\x01\x02\x03\x04\x05\x0D\x01\x06\x00\x70\x0C\x40\x00\x03\x07\x01\x00\x02\x00'
-                )
-            )
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0D:05:04:03:02:01'))
 
-            # Set the LE Address to 0D:05:04:03:02:01
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x05\x20\x06\x01\x02\x03\x04\x05\x0D'
-                )
-            )
-            # Set LE Advertising parameters
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x06\x20\x0F\x80\x00\x00\x04\x00\x01\x00\xA1\xA2\xA3\xA4\xA5\xA6\x07\x00'
-                )
-            )
-            # Set LE Advertising data
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x08\x20\x20\x0C\x0B\x09Im_The_DUT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
-                )
-            )
-            # Enable Advertising
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x0A\x20\x01\x01'
-                )
-            )
-            # LeConnectionComplete TODO: Extract the handle
-            cert_hci_event_asserts.assert_event_occurs(
-                lambda packet: b'\x3e\x13\x01\x00' in packet.payload
-            )
-            # LeConnectionComplete TODO: Extract the handle
-            hci_event_asserts.assert_event_occurs(
-                lambda packet: b'\x3e\x13\x01\x00' in packet.payload
-            )
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_The_DUT'))
+
+            self.send_dut_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_The_D'))
+
+            self.send_dut_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.send_dut_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
+
+            conn_handle = 0xfff
+
+            def payload_handle(packet):
+                packet_bytes = packet.payload
+                if b'\x3e\x13\x01\x00' in packet_bytes:
+                    nonlocal conn_handle
+                    cc_view = hci_packets.LeConnectionCompleteView(
+                        hci_packets.LeMetaEventView(
+                            hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
+                    conn_handle = cc_view.GetConnectionHandle()
+                    return True
+                return False
+
+            assertThat(cert_hci_event_stream).emits(payload_handle)
+            cert_handle = conn_handle
+            conn_handle = 0xfff
+            assertThat(hci_event_stream).emits(payload_handle)
+            dut_handle = conn_handle
+
             # Send ACL Data
-            self.device_under_test.hal.SendHciAcl(
-                hal_facade_pb2.HciAclPacket(
-                    payload=b'\xfe\x0e\x0b\x00SomeAclData'
-                )
-            )
-            # Send ACL Data
-            self.cert_device.hal.SendHciAcl(
-                hal_facade_pb2.HciAclPacket(
-                    payload=b'\xfe\x0e\x0f\x00SomeMoreAclData'
-                )
-            )
-            cert_acl_data_asserts.assert_event_occurs(
-                lambda packet: b'\xfe\x0e\x0b\x00SomeAclData' in packet.payload
-            )
-            acl_data_asserts.assert_event_occurs(
-                lambda packet: b'\xfe\x0e\x0f\x00SomeMoreAclData' in packet.payload
-            )
+            self.send_dut_acl_data(dut_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                                   hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeAclData'))
+            self.send_cert_acl_data(cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                                    hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeMoreAclData'))
 
-    def test_le_white_list_connection_cert_advertises(self):
-        with EventCallbackStream(self.device_under_test.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream, \
-                EventCallbackStream(self.cert_device.hal.FetchHciEvent(empty_pb2.Empty())) as cert_hci_event_stream:
-            hci_event_asserts = EventAsserts(hci_event_stream)
-            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+            assertThat(cert_acl_data_stream).emits(lambda packet: b'SomeAclData' in packet.payload)
+            assertThat(acl_data_stream).emits(lambda packet: b'SomeMoreAclData' in packet.payload)
 
-            # Set the LE Address to 0D:05:04:03:02:01
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x05\x20\x06\x01\x02\x03\x04\x05\x0D'
-                )
-            )
-            # Add the cert device to the white list (Random 0C:05:04:03:02:01)
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x11\x20\x07\x01\x01\x02\x03\x04\x05\x0C'
-                )
-            )
-            # Connect using the white list
-            self.device_under_test.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x0D\x20\x19\x11\x01\x22\x02\x01\x00\xA1\xA2\xA3\xA4\xA5\xA6\x01\x06\x00\x70\x0C\x40\x00\x03\x07\x01\x00\x02\x00'
-                )
-            )
+    def test_le_connect_list_connection_cert_advertises(self):
+        with EventStream(self.dut.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream, \
+            EventStream(self.cert.hal.FetchHciEvent(empty_pb2.Empty())) as cert_hci_event_stream:
 
-            # Set the LE Address to 0C:05:04:03:02:01
-            self.cert_device.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x05\x20\x06\x01\x02\x03\x04\x05\x0C'
-                )
-            )
-            # Set LE Advertising parameters
-            self.cert_device.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x06\x20\x0F\x00\x02\x00\x03\x00\x01\x00\xA1\xA2\xA3\xA4\xA5\xA6\x07\x00'
-                )
-            )
-            # Set LE Advertising data
-            self.cert_device.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x08\x20\x20\x0C\x0A\x09Im_A_Cert\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
-                )
-            )
-            # Enable Advertising
-            self.cert_device.hal.SendHciCommand(
-                hal_facade_pb2.HciCommandPacket(
-                    payload=b'\x0A\x20\x01\x01'
-                )
-            )
+            # DUT Connects
+            self.send_dut_hci_command(hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
+            self.send_dut_hci_command(
+                hci_packets.LeAddDeviceToConnectListBuilder(hci_packets.ConnectListAddressType.RANDOM,
+                                                            '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.send_dut_hci_command(
+                hci_packets.LeExtendedCreateConnectionBuilder(hci_packets.InitiatorFilterPolicy.USE_CONNECT_LIST,
+                                                              hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                                                              hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                                                              'BA:D5:A4:A3:A2:A1', 1, [phy_scan_params]))
+
+            # CERT Advertises
+            advertising_handle = 1
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    512,
+                    768,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    'A6:A5:A4:A3:A2:A1',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0x7F,
+                    0,  # SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ))
+
+            self.send_cert_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.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = 1
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
+
             # LeConnectionComplete
-            cert_hci_event_asserts.assert_event_occurs(
-                lambda packet: b'\x3e\x13\x01\x00' in packet.payload
-            )
-            # LeConnectionComplete
-            hci_event_asserts.assert_event_occurs(
-                lambda packet: b'\x3e\x13\x01\x00' in packet.payload
-            )
+            cert_hci_event_stream.assert_event_occurs(
+                lambda packet: b'\x3e\x13\x01\x00' in packet.payload, timeout=timedelta(seconds=20))
+            hci_event_stream.assert_event_occurs(
+                lambda packet: b'\x3e\x13\x01\x00' in packet.payload, timeout=timedelta(seconds=20))
diff --git a/gd/hal/facade.cc b/gd/hal/facade.cc
index 6469fb1..8e00f10 100644
--- a/gd/hal/facade.cc
+++ b/gd/hal/facade.cc
@@ -16,15 +16,12 @@
 
 #include "hal/facade.h"
 
-#include <condition_variable>
 #include <memory>
 #include <mutex>
 
 #include "grpc/grpc_event_queue.h"
 #include "hal/facade.grpc.pb.h"
 #include "hal/hci_hal.h"
-#include "hal/serialize_packet.h"
-#include "hci/hci_packets.h"
 
 using ::grpc::ServerAsyncResponseWriter;
 using ::grpc::ServerAsyncWriter;
@@ -43,47 +40,10 @@
     hal_->unregisterIncomingPacketCallback();
   }
 
-  ::grpc::Status SendHciResetCommand(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                     ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(mutex_);
-    can_send_hci_command_ = false;
-    hal_->sendHciCommand(SerializePacket(hci::ResetBuilder::Create()));
-    while (!can_send_hci_command_) {
-      cv_.wait(lock);
-    }
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SetLoopbackMode(::grpc::ServerContext* context, const ::bluetooth::hal::LoopbackModeSettings* request,
-                                 ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(mutex_);
-    can_send_hci_command_ = false;
-    bool enable = request->enable();
-    hal_->sendHciCommand(SerializePacket(hci::WriteLoopbackModeBuilder::Create(
-        enable ? hci::LoopbackMode::ENABLE_LOCAL : hci::LoopbackMode::NO_LOOPBACK)));
-    while (!can_send_hci_command_) {
-      cv_.wait(lock);
-    }
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SetInquiry(::grpc::ServerContext* context, const ::bluetooth::hal::InquirySettings* request,
-                            ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(mutex_);
-    can_send_hci_command_ = false;
-    hci::Lap lap;
-    lap.lap_ = 0x33;
-
-    hal_->sendHciCommand(SerializePacket(hci::InquiryBuilder::Create(lap, static_cast<uint8_t>(request->length()),
-                                                                     static_cast<uint8_t>(request->num_responses()))));
-    while (!can_send_hci_command_) {
-      cv_.wait(lock);
-    }
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendHciCommand(::grpc::ServerContext* context, const ::bluetooth::hal::HciCommandPacket* request,
-                                ::google::protobuf::Empty* response) override {
+  ::grpc::Status SendHciCommand(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::hal::HciCommandPacket* request,
+      ::google::protobuf::Empty* response) override {
     std::unique_lock<std::mutex> lock(mutex_);
     can_send_hci_command_ = false;
     std::string req_string = request->payload();
@@ -94,32 +54,42 @@
     return ::grpc::Status::OK;
   }
 
-  ::grpc::Status SendHciAcl(::grpc::ServerContext* context, const ::bluetooth::hal::HciAclPacket* request,
-                            ::google::protobuf::Empty* response) override {
+  ::grpc::Status SendHciAcl(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::hal::HciAclPacket* request,
+      ::google::protobuf::Empty* response) override {
     std::string req_string = request->payload();
     hal_->sendAclData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
     return ::grpc::Status::OK;
   }
 
-  ::grpc::Status SendHciSco(::grpc::ServerContext* context, const ::bluetooth::hal::HciScoPacket* request,
-                            ::google::protobuf::Empty* response) override {
+  ::grpc::Status SendHciSco(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::hal::HciScoPacket* request,
+      ::google::protobuf::Empty* response) override {
     std::string req_string = request->payload();
     hal_->sendScoData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
     return ::grpc::Status::OK;
   }
 
-  ::grpc::Status FetchHciEvent(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                               ::grpc::ServerWriter<HciEventPacket>* writer) override {
+  ::grpc::Status FetchHciEvent(
+      ::grpc::ServerContext* context,
+      const ::google::protobuf::Empty* request,
+      ::grpc::ServerWriter<HciEventPacket>* writer) override {
     return pending_hci_events_.RunLoop(context, writer);
   };
 
-  ::grpc::Status FetchHciAcl(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                             ::grpc::ServerWriter<HciAclPacket>* writer) override {
+  ::grpc::Status FetchHciAcl(
+      ::grpc::ServerContext* context,
+      const ::google::protobuf::Empty* request,
+      ::grpc::ServerWriter<HciAclPacket>* writer) override {
     return pending_acl_events_.RunLoop(context, writer);
   };
 
-  ::grpc::Status FetchHciSco(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                             ::grpc::ServerWriter<HciScoPacket>* writer) override {
+  ::grpc::Status FetchHciSco(
+      ::grpc::ServerContext* context,
+      const ::google::protobuf::Empty* request,
+      ::grpc::ServerWriter<HciScoPacket>* writer) override {
     return pending_sco_events_.RunLoop(context, writer);
   };
 
diff --git a/gd/hal/facade.h b/gd/hal/facade.h
index fa28d10..ed846d5 100644
--- a/gd/hal/facade.h
+++ b/gd/hal/facade.h
@@ -16,11 +16,11 @@
 
 #pragma once
 
+#include <grpc++/grpc++.h>
+
 #include <list>
 #include <mutex>
 
-#include <grpc++/grpc++.h>
-
 #include "grpc/grpc_module.h"
 #include "hal/hci_hal.h"
 
diff --git a/gd/hal/facade.proto b/gd/hal/facade.proto
index 0297f74..539e810 100644
--- a/gd/hal/facade.proto
+++ b/gd/hal/facade.proto
@@ -3,12 +3,8 @@
 package bluetooth.hal;
 
 import "google/protobuf/empty.proto";
-import "facade/common.proto";
 
 service HciHalFacade {
-  rpc SendHciResetCommand(google.protobuf.Empty) returns (google.protobuf.Empty) {}
-  rpc SetLoopbackMode(LoopbackModeSettings) returns (google.protobuf.Empty) {}
-  rpc SetInquiry(InquirySettings) returns (google.protobuf.Empty) {}
   rpc SendHciCommand(HciCommandPacket) returns (google.protobuf.Empty) {}
   rpc SendHciAcl(HciAclPacket) returns (google.protobuf.Empty) {}
   rpc SendHciSco(HciScoPacket) returns (google.protobuf.Empty) {}
@@ -18,19 +14,6 @@
   rpc FetchHciSco(google.protobuf.Empty) returns (stream HciScoPacket) {}
 }
 
-message LoopbackModeSettings {
-  bool enable = 1;
-}
-
-message ScanModeSettings {
-  uint32 mode = 1;
-}
-
-message InquirySettings {
-  uint32 length = 1;
-  uint32 num_responses = 2;
-}
-
 message HciEventPacket {
   bytes payload = 1;
 }
diff --git a/gd/hal/fuzz/fuzz_hci_hal.cc b/gd/hal/fuzz/fuzz_hci_hal.cc
new file mode 100644
index 0000000..06b4230
--- /dev/null
+++ b/gd/hal/fuzz/fuzz_hci_hal.cc
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#include "hal/fuzz/fuzz_hci_hal.h"
+
+#include "fuzz/helpers.h"
+#include "hci/fuzz/status_vs_complete_commands.h"
+
+namespace bluetooth {
+namespace hal {
+namespace fuzz {
+using bluetooth::fuzz::GetArbitraryBytes;
+
+void FuzzHciHal::registerIncomingPacketCallback(HciHalCallbacks* callbacks) {
+  callbacks_ = callbacks;
+}
+
+void FuzzHciHal::unregisterIncomingPacketCallback() {
+  callbacks_ = nullptr;
+}
+
+void FuzzHciHal::injectArbitrary(FuzzedDataProvider& fdp) {
+  const uint8_t action = fdp.ConsumeIntegralInRange(0, 3);
+  switch (action) {
+    case 1:
+      injectAclData(GetArbitraryBytes(&fdp));
+      break;
+    case 2:
+      injectHciEvent(GetArbitraryBytes(&fdp));
+      break;
+    case 3:
+      injectScoData(GetArbitraryBytes(&fdp));
+      break;
+  }
+}
+
+void FuzzHciHal::sendHciCommand(HciPacket packet) {
+  hci::CommandPacketView command = hci::CommandPacketView::FromBytes(packet);
+  if (!command.IsValid()) {
+    return;
+  }
+
+  waiting_opcode_ = command.GetOpCode();
+  waiting_for_status_ = hci::fuzz::uses_command_status(waiting_opcode_);
+}
+
+void FuzzHciHal::injectHciEvent(std::vector<uint8_t> data) {
+  hci::EventPacketView event = hci::EventPacketView::FromBytes(data);
+  if (!event.IsValid()) {
+    return;
+  }
+
+  hci::CommandCompleteView complete = hci::CommandCompleteView::Create(event);
+  if (complete.IsValid()) {
+    if (waiting_for_status_ || complete.GetCommandOpCode() != waiting_opcode_) {
+      return;
+    }
+  } else if (!waiting_for_status_) {
+    return;
+  }
+
+  hci::CommandStatusView status = hci::CommandStatusView::Create(event);
+  if (status.IsValid()) {
+    if (!waiting_for_status_ || status.GetCommandOpCode() != waiting_opcode_) {
+      return;
+    }
+  } else if (waiting_for_status_) {
+    return;
+  }
+
+  callbacks_->hciEventReceived(data);
+}
+
+void FuzzHciHal::injectAclData(std::vector<uint8_t> data) {
+  hci::AclPacketView aclPacket = hci::AclPacketView::FromBytes(data);
+  if (!aclPacket.IsValid()) {
+    return;
+  }
+
+  callbacks_->aclDataReceived(data);
+}
+
+void FuzzHciHal::injectScoData(std::vector<uint8_t> data) {
+  hci::ScoPacketView scoPacket = hci::ScoPacketView::FromBytes(data);
+  if (!scoPacket.IsValid()) {
+    return;
+  }
+
+  callbacks_->scoDataReceived(data);
+}
+
+const ModuleFactory FuzzHciHal::Factory = ModuleFactory([]() { return new FuzzHciHal(); });
+
+}  // namespace fuzz
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/fuzz/fuzz_hci_hal.h b/gd/hal/fuzz/fuzz_hci_hal.h
new file mode 100644
index 0000000..e400661
--- /dev/null
+++ b/gd/hal/fuzz/fuzz_hci_hal.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "fuzz/helpers.h"
+#include "hal/hci_hal.h"
+#include "hci/hci_packets.h"
+
+namespace bluetooth {
+namespace hal {
+namespace fuzz {
+
+class FuzzHciHal : public HciHal {
+ public:
+  void registerIncomingPacketCallback(HciHalCallbacks* callbacks) override;
+  void unregisterIncomingPacketCallback() override;
+
+  void sendHciCommand(HciPacket command) override;
+  void sendAclData(HciPacket packet) override {}
+  void sendScoData(HciPacket packet) override {}
+
+  void injectArbitrary(FuzzedDataProvider& fdp);
+
+  std::string ToString() const override {
+    return "HciHalFuzz";
+  }
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override {}
+  void Stop() override {}
+
+ private:
+  void injectAclData(std::vector<uint8_t> data);
+  void injectHciEvent(std::vector<uint8_t> data);
+  void injectScoData(std::vector<uint8_t> data);
+
+  HciHalCallbacks* callbacks_;
+  hci::OpCode waiting_opcode_;
+  bool waiting_for_status_;
+};
+
+}  // namespace fuzz
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal.h b/gd/hal/hci_hal.h
index f95a4f9..23ac2b5 100644
--- a/gd/hal/hci_hal.h
+++ b/gd/hal/hci_hal.h
@@ -56,6 +56,7 @@
 // Abstraction Layer (HAL). Dealing only in HCI packets and events simplifies
 // the stack and abstracts away power management, initialization, and other
 // implementation-specific details related to the hardware.
+// LINT.IfChange
 class HciHal : public ::bluetooth::Module {
  public:
   static const ModuleFactory Factory;
@@ -88,6 +89,7 @@
   // Packets must be processed in order.
   virtual void sendScoData(HciPacket data) = 0;
 };
+// LINT.ThenChange(fuzz/fuzz_hci_hal.h)
 
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/gd/hal/hci_hal_android_hidl.cc b/gd/hal/hci_hal_android_hidl.cc
index 7e45d89..930df1e 100644
--- a/gd/hal/hci_hal_android_hidl.cc
+++ b/gd/hal/hci_hal_android_hidl.cc
@@ -14,16 +14,15 @@
  * limitations under the License.
  */
 
-#include "hal/hci_hal.h"
-
-#include <stdlib.h>
-#include <vector>
-#include <future>
-
 #include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
 #include <android/hardware/bluetooth/1.0/IBluetoothHciCallbacks.h>
 #include <android/hardware/bluetooth/1.0/types.h>
+#include <stdlib.h>
 
+#include <future>
+#include <vector>
+
+#include "hal/hci_hal.h"
 #include "hal/snoop_logger.h"
 #include "os/log.h"
 
@@ -50,8 +49,7 @@
 
 class InternalHciCallbacks : public IBluetoothHciCallbacks {
  public:
-  InternalHciCallbacks(SnoopLogger* btsnoop_logger)
-      : btsnoop_logger_(btsnoop_logger) {
+  InternalHciCallbacks(SnoopLogger* btsnoop_logger) : btsnoop_logger_(btsnoop_logger) {
     init_promise_ = new std::promise<void>();
   }
 
@@ -76,8 +74,7 @@
 
   Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) {
     std::vector<uint8_t> received_hci_packet(event.begin(), event.end());
-    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING,
-                             SnoopLogger::PacketType::EVT);
+    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
     if (callback_ != nullptr) {
       callback_->hciEventReceived(std::move(received_hci_packet));
     }
@@ -86,8 +83,7 @@
 
   Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) {
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
-    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING,
-                             SnoopLogger::PacketType::ACL);
+    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
     if (callback_ != nullptr) {
       callback_->aclDataReceived(std::move(received_hci_packet));
     }
@@ -96,8 +92,7 @@
 
   Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) {
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
-    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING,
-                             SnoopLogger::PacketType::SCO);
+    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::SCO);
     if (callback_ != nullptr) {
       callback_->scoDataReceived(std::move(received_hci_packet));
     }
@@ -177,9 +172,7 @@
   SnoopLogger* btsnoop_logger_;
 };
 
-const ModuleFactory HciHal::Factory = ModuleFactory([]() {
-  return new HciHalHidl();
-});
+const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHidl(); });
 
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/gd/hal/hci_hal_android_hidl_test.cc b/gd/hal/hci_hal_android_hidl_test.cc
index 5b8b68d..1bdff58 100644
--- a/gd/hal/hci_hal_android_hidl_test.cc
+++ b/gd/hal/hci_hal_android_hidl_test.cc
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-#include "hal/hci_hal.h"
+#include <gtest/gtest.h>
 
 #include <chrono>
 #include <future>
 
-#include <gtest/gtest.h>
-
+#include "hal/hci_hal.h"
 #include "os/thread.h"
 
 using ::bluetooth::os::Thread;
diff --git a/gd/hal/hci_hal_host_rootcanal.cc b/gd/hal/hci_hal_host_rootcanal.cc
index 11809ba..00d453b 100644
--- a/gd/hal/hci_hal_host_rootcanal.cc
+++ b/gd/hal/hci_hal_host_rootcanal.cc
@@ -15,16 +15,18 @@
  */
 
 #include "hal/hci_hal_host_rootcanal.h"
-#include "hal/hci_hal.h"
 
 #include <netdb.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
+
+#include <chrono>
 #include <csignal>
 #include <mutex>
 #include <queue>
 
+#include "hal/hci_hal.h"
 #include "hal/snoop_logger.h"
 #include "os/log.h"
 #include "os/reactor.h"
@@ -42,7 +44,7 @@
 constexpr uint8_t kHciAclHeaderSize = 4;
 constexpr uint8_t kHciScoHeaderSize = 3;
 constexpr uint8_t kHciEvtHeaderSize = 2;
-constexpr int kBufSize = 1024;
+constexpr int kBufSize = 1024 + 4 + 1;  // DeviceProperties::acl_data_packet_size_ + ACL header + H4 header
 
 int ConnectToRootCanal(const std::string& server, int port) {
   int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
@@ -92,18 +94,28 @@
 class HciHalHostRootcanal : public HciHal {
  public:
   void registerIncomingPacketCallback(HciHalCallbacks* callback) override {
-    std::lock_guard<std::mutex> lock(mutex_);
-    ASSERT(incoming_packet_callback_ == nullptr && callback != nullptr);
-    incoming_packet_callback_ = callback;
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    LOG_INFO("%s before", __func__);
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      ASSERT(incoming_packet_callback_ == nullptr && callback != nullptr);
+      incoming_packet_callback_ = callback;
+    }
+    LOG_INFO("%s after", __func__);
   }
 
   void unregisterIncomingPacketCallback() override {
-    std::lock_guard<std::mutex> lock(mutex_);
-    incoming_packet_callback_ = nullptr;
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    LOG_INFO("%s before", __func__);
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      incoming_packet_callback_ = nullptr;
+    }
+    LOG_INFO("%s after", __func__);
   }
 
   void sendHciCommand(HciPacket command) override {
-    std::lock_guard<std::mutex> lock(mutex_);
+    std::lock_guard<std::mutex> lock(api_mutex_);
     ASSERT(sock_fd_ != INVALID_FD);
     std::vector<uint8_t> packet = std::move(command);
     btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);
@@ -112,7 +124,7 @@
   }
 
   void sendAclData(HciPacket data) override {
-    std::lock_guard<std::mutex> lock(mutex_);
+    std::lock_guard<std::mutex> lock(api_mutex_);
     ASSERT(sock_fd_ != INVALID_FD);
     std::vector<uint8_t> packet = std::move(data);
     btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);
@@ -121,7 +133,7 @@
   }
 
   void sendScoData(HciPacket data) override {
-    std::lock_guard<std::mutex> lock(mutex_);
+    std::lock_guard<std::mutex> lock(api_mutex_);
     ASSERT(sock_fd_ != INVALID_FD);
     std::vector<uint8_t> packet = std::move(data);
     btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);
@@ -135,53 +147,65 @@
   }
 
   void Start() override {
-    std::lock_guard<std::mutex> lock(mutex_);
+    std::lock_guard<std::mutex> lock(api_mutex_);
     ASSERT(sock_fd_ == INVALID_FD);
     sock_fd_ = ConnectToRootCanal(config_->GetServerAddress(), config_->GetPort());
     ASSERT(sock_fd_ != INVALID_FD);
     reactable_ = hci_incoming_thread_.GetReactor()->Register(
-        sock_fd_, common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
+        sock_fd_,
+        common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
         common::Closure());
     btsnoop_logger_ = GetDependency<SnoopLogger>();
     LOG_INFO("Rootcanal HAL opened successfully");
   }
 
   void Stop() override {
-    std::lock_guard<std::mutex> lock(mutex_);
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    LOG_INFO("Rootcanal HAL is closing");
     if (reactable_ != nullptr) {
       hci_incoming_thread_.GetReactor()->Unregister(reactable_);
+      LOG_INFO("Rootcanal HAL is stopping, start waiting for last callback");
+      // Wait up to 1 second for the last incoming packet callback to finish
+      hci_incoming_thread_.GetReactor()->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
+      LOG_INFO("Rootcanal HAL is stopping, finished waiting for last callback");
       ASSERT(sock_fd_ != INVALID_FD);
     }
     reactable_ = nullptr;
-    incoming_packet_callback_ = nullptr;
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      incoming_packet_callback_ = nullptr;
+    }
     ::close(sock_fd_);
     sock_fd_ = INVALID_FD;
     LOG_INFO("Rootcanal HAL is closed");
   }
 
  private:
-  std::mutex mutex_;
+  // Held when APIs are called, NOT to be held during callbacks
+  std::mutex api_mutex_;
   HciHalHostRootcanalConfig* config_ = HciHalHostRootcanalConfig::Get();
   HciHalCallbacks* incoming_packet_callback_ = nullptr;
+  std::mutex incoming_packet_callback_mutex_;
   int sock_fd_ = INVALID_FD;
   bluetooth::os::Thread hci_incoming_thread_ =
       bluetooth::os::Thread("hci_incoming_thread", bluetooth::os::Thread::Priority::NORMAL);
   bluetooth::os::Reactor::Reactable* reactable_ = nullptr;
   std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
-  SnoopLogger* btsnoop_logger_;
+  SnoopLogger* btsnoop_logger_ = nullptr;
 
   void write_to_rootcanal_fd(HciPacket packet) {
     // TODO: replace this with new queue when it's ready
     hci_outgoing_queue_.emplace(packet);
     if (hci_outgoing_queue_.size() == 1) {
       hci_incoming_thread_.GetReactor()->ModifyRegistration(
-          reactable_, common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
+          reactable_,
+          common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
           common::Bind(&HciHalHostRootcanal::send_packet_ready, common::Unretained(this)));
     }
   }
 
   void send_packet_ready() {
-    std::lock_guard<std::mutex> lock(this->mutex_);
+    std::lock_guard<std::mutex> lock(this->api_mutex_);
     auto packet_to_send = this->hci_outgoing_queue_.front();
     auto bytes_written = write(this->sock_fd_, (void*)packet_to_send.data(), packet_to_send.size());
     this->hci_outgoing_queue_.pop();
@@ -190,24 +214,27 @@
     }
     if (hci_outgoing_queue_.empty()) {
       this->hci_incoming_thread_.GetReactor()->ModifyRegistration(
-          this->reactable_, common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
+          this->reactable_,
+          common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
           common::Closure());
     }
   }
 
   void incoming_packet_received() {
-    if (incoming_packet_callback_ == nullptr) {
-      LOG_INFO("Dropping a packet");
-      return;
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      if (incoming_packet_callback_ == nullptr) {
+        LOG_INFO("Dropping a packet");
+        return;
+      }
     }
-
     uint8_t buf[kBufSize] = {};
 
     ssize_t received_size;
     RUN_NO_INTR(received_size = recv(sock_fd_, buf, kH4HeaderSize, 0));
     ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
     if (received_size == 0) {
-      LOG_WARN("Can't read H4 header.");
+      LOG_WARN("Can't read H4 header. EOF received");
       raise(SIGINT);
       return;
     }
@@ -219,18 +246,26 @@
 
       uint8_t hci_evt_parameter_total_length = buf[2];
       ssize_t payload_size;
-      RUN_NO_INTR(payload_size =
-                      recv(sock_fd_, buf + kH4HeaderSize + kHciEvtHeaderSize, hci_evt_parameter_total_length, 0));
+      RUN_NO_INTR(
+          payload_size = recv(sock_fd_, buf + kH4HeaderSize + kHciEvtHeaderSize, hci_evt_parameter_total_length, 0));
       ASSERT_LOG(payload_size != -1, "Can't receive from socket: %s", strerror(errno));
-      ASSERT_LOG(payload_size == hci_evt_parameter_total_length,
-                 "malformed HCI event total parameter size received: %zu != %d", payload_size,
-                 hci_evt_parameter_total_length);
+      ASSERT_LOG(
+          payload_size == hci_evt_parameter_total_length,
+          "malformed HCI event total parameter size received: %zu != %d",
+          payload_size,
+          hci_evt_parameter_total_length);
 
       HciPacket receivedHciPacket;
       receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciEvtHeaderSize + payload_size);
-      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
-                               SnoopLogger::PacketType::EVT);
-      incoming_packet_callback_->hciEventReceived(receivedHciPacket);
+      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
+      {
+        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+        if (incoming_packet_callback_ == nullptr) {
+          LOG_INFO("Dropping an event after processing");
+          return;
+        }
+        incoming_packet_callback_->hciEventReceived(receivedHciPacket);
+      }
     }
 
     if (buf[0] == kH4Acl) {
@@ -238,19 +273,28 @@
       ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
       ASSERT_LOG(received_size == kHciAclHeaderSize, "malformed ACL header received");
 
-      uint16_t hci_acl_data_total_length = buf[4] * 256 + buf[3];
+      uint16_t hci_acl_data_total_length = (buf[4] << 8) + buf[3];
       int payload_size;
       RUN_NO_INTR(payload_size = recv(sock_fd_, buf + kH4HeaderSize + kHciAclHeaderSize, hci_acl_data_total_length, 0));
       ASSERT_LOG(payload_size != -1, "Can't receive from socket: %s", strerror(errno));
-      ASSERT_LOG(payload_size == hci_acl_data_total_length, "malformed ACL length received: %d != %d", payload_size,
-                 hci_acl_data_total_length);
+      ASSERT_LOG(
+          payload_size == hci_acl_data_total_length,
+          "malformed ACL length received: %d != %d",
+          payload_size,
+          hci_acl_data_total_length);
       ASSERT_LOG(hci_acl_data_total_length <= kBufSize - kH4HeaderSize - kHciAclHeaderSize, "packet too long");
 
       HciPacket receivedHciPacket;
       receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciAclHeaderSize + payload_size);
-      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
-                               SnoopLogger::PacketType::ACL);
-      incoming_packet_callback_->aclDataReceived(receivedHciPacket);
+      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
+      {
+        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+        if (incoming_packet_callback_ == nullptr) {
+          LOG_INFO("Dropping an ACL packet after processing");
+          return;
+        }
+        incoming_packet_callback_->aclDataReceived(receivedHciPacket);
+      }
     }
 
     if (buf[0] == kH4Sco) {
@@ -266,17 +310,21 @@
 
       HciPacket receivedHciPacket;
       receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciScoHeaderSize + payload_size);
-      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
-                               SnoopLogger::PacketType::SCO);
-      incoming_packet_callback_->scoDataReceived(receivedHciPacket);
+      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::SCO);
+      {
+        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+        if (incoming_packet_callback_ == nullptr) {
+          LOG_INFO("Dropping a SCO packet after processing");
+          return;
+        }
+        incoming_packet_callback_->scoDataReceived(receivedHciPacket);
+      }
     }
     memset(buf, 0, kBufSize);
   }
 };
 
-const ModuleFactory HciHal::Factory = ModuleFactory([]() {
-  return new HciHalHostRootcanal();
-});
+const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHostRootcanal(); });
 
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/gd/hal/hci_hal_host_rootcanal_test.cc b/gd/hal/hci_hal_host_rootcanal_test.cc
index 9783e84..aa92b84 100644
--- a/gd/hal/hci_hal_host_rootcanal_test.cc
+++ b/gd/hal/hci_hal_host_rootcanal_test.cc
@@ -15,24 +15,24 @@
  */
 
 #include "hal/hci_hal_host_rootcanal.h"
-#include "hal/hci_hal.h"
-#include "hal/serialize_packet.h"
 
 #include <fcntl.h>
+#include <gtest/gtest.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
+
 #include <cstring>
 #include <queue>
 #include <thread>
 #include <utility>
 #include <vector>
 
-#include <gtest/gtest.h>
-
+#include "hal/hci_hal.h"
+#include "hal/serialize_packet.h"
 #include "os/log.h"
 #include "os/thread.h"
 #include "os/utils.h"
@@ -172,9 +172,9 @@
 
 void check_packet_equal(std::pair<uint8_t, HciPacket> hci_packet1_type_data_pair, H4Packet h4_packet2) {
   auto packet1_hci_size = hci_packet1_type_data_pair.second.size();
-  EXPECT_EQ(packet1_hci_size + 1, h4_packet2.size());
-  EXPECT_EQ(hci_packet1_type_data_pair.first, h4_packet2[0]);
-  EXPECT_EQ(memcmp(hci_packet1_type_data_pair.second.data(), h4_packet2.data() + 1, packet1_hci_size), 0);
+  ASSERT_EQ(packet1_hci_size + 1, h4_packet2.size());
+  ASSERT_EQ(hci_packet1_type_data_pair.first, h4_packet2[0]);
+  ASSERT_EQ(memcmp(hci_packet1_type_data_pair.second.data(), h4_packet2.data() + 1, packet1_hci_size), 0);
 }
 
 HciPacket make_sample_hci_cmd_pkt(uint8_t parameter_total_length) {
@@ -223,6 +223,16 @@
   return pkt;
 }
 
+size_t read_with_retry(int socket, uint8_t* data, size_t length) {
+  size_t bytes_read = 0;
+  ssize_t bytes_read_current = 0;
+  do {
+    bytes_read_current = read(socket, data + bytes_read, length - bytes_read);
+    bytes_read += bytes_read_current;
+  } while (length > bytes_read && bytes_read_current > 0);
+  return bytes_read;
+}
+
 TEST_F(HciHalRootcanalTest, init_and_close) {}
 
 TEST_F(HciHalRootcanalTest, receive_hci_evt) {
@@ -319,9 +329,9 @@
   hal_->sendHciCommand(hci_data);
   H4Packet read_buf(1 + 2 + 1 + hci_cmd_param_size);
   SetFakeServerSocketToBlocking();
-  auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
+  auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
 
-  EXPECT_EQ(size_read, 1 + hci_data.size());
+  ASSERT_EQ(size_read, 1 + hci_data.size());
   check_packet_equal({kH4Command, hci_data}, read_buf);
 }
 
@@ -331,9 +341,9 @@
   hal_->sendAclData(acl_packet);
   H4Packet read_buf(1 + 2 + 2 + acl_payload_size);
   SetFakeServerSocketToBlocking();
-  auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
+  auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
 
-  EXPECT_EQ(size_read, 1 + acl_packet.size());
+  ASSERT_EQ(size_read, 1 + acl_packet.size());
   check_packet_equal({kH4Acl, acl_packet}, read_buf);
 }
 
@@ -343,9 +353,9 @@
   hal_->sendScoData(sco_packet);
   H4Packet read_buf(1 + 3 + sco_payload_size);
   SetFakeServerSocketToBlocking();
-  auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
+  auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
 
-  EXPECT_EQ(size_read, 1 + sco_packet.size());
+  ASSERT_EQ(size_read, 1 + sco_packet.size());
   check_packet_equal({kH4Sco, sco_packet}, read_buf);
 }
 
@@ -359,8 +369,8 @@
   H4Packet read_buf(1 + 2 + 2 + acl_payload_size);
   SetFakeServerSocketToBlocking();
   for (int i = 0; i < num_packets; i++) {
-    auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
-    EXPECT_EQ(size_read, 1 + acl_packet.size());
+    auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
+    ASSERT_EQ(size_read, 1 + acl_packet.size());
     check_packet_equal({kH4Acl, acl_packet}, read_buf);
   }
 }
@@ -373,8 +383,8 @@
   for (int i = 0; i < num_packets; i++) {
     hal_->sendAclData(acl_packet);
     H4Packet read_buf(1 + 2 + 2 + acl_payload_size);
-    auto size_read = read(fake_server_socket_, read_buf.data(), read_buf.size());
-    EXPECT_EQ(size_read, 1 + acl_packet.size());
+    auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
+    ASSERT_EQ(size_read, 1 + acl_packet.size());
     check_packet_equal({kH4Acl, acl_packet}, read_buf);
   }
 }
@@ -382,7 +392,7 @@
 TEST(HciHalHidlTest, serialize) {
   std::vector<uint8_t> bytes = {1, 2, 3, 4, 5, 6, 7, 8, 9};
   auto packet_bytes = hal::SerializePacket(std::unique_ptr<packet::BasePacketBuilder>(new packet::RawBuilder(bytes)));
-  EXPECT_EQ(bytes, packet_bytes);
+  ASSERT_EQ(bytes, packet_bytes);
 }
 }  // namespace
 }  // namespace hal
diff --git a/gd/hal/snoop_logger.cc b/gd/hal/snoop_logger.cc
index 3574b17..6d5242d 100644
--- a/gd/hal/snoop_logger.cc
+++ b/gd/hal/snoop_logger.cc
@@ -18,6 +18,7 @@
 
 #include <arpa/inet.h>
 #include <netinet/in.h>
+
 #include <bitset>
 #include <chrono>
 
@@ -132,9 +133,7 @@
 
 std::string SnoopLogger::file_path = SnoopLogger::DefaultFilePath;
 
-const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() {
-  return new SnoopLogger();
-});
+const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() { return new SnoopLogger(); });
 
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/gd/hci/Android.bp b/gd/hci/Android.bp
index c656b56..b889c12 100644
--- a/gd/hci/Android.bp
+++ b/gd/hci/Android.bp
@@ -1,15 +1,19 @@
 filegroup {
     name: "BluetoothHciSources",
     srcs: [
+        "acl_manager/acl_connection.cc",
+        "acl_manager/classic_acl_connection.cc",
+        "acl_manager/le_acl_connection.cc",
+        "acl_manager/round_robin_scheduler.cc",
+        "acl_manager/acl_fragmenter.cc",
         "acl_manager.cc",
-        "acl_fragmenter.cc",
         "address.cc",
-        "classic_security_manager.cc",
         "class_of_device.cc",
         "controller.cc",
         "device.cc",
         "device_database.cc",
         "hci_layer.cc",
+        "le_address_manager.cc",
         "le_advertising_manager.cc",
         "le_scanning_manager.cc",
     ],
@@ -19,17 +23,18 @@
     name: "BluetoothHciTestSources",
     srcs: [
         "acl_builder_test.cc",
+        "acl_manager/round_robin_scheduler_test.cc",
         "acl_manager_test.cc",
         "address_unittest.cc",
         "address_with_type_test.cc",
         "class_of_device_unittest.cc",
-        "classic_security_manager_test.cc",
         "controller_test.cc",
         "device_test.cc",
         "device_database_test.cc",
         "dual_device_test.cc",
         "hci_layer_test.cc",
         "hci_packets_test.cc",
+        "le_address_manager_test.cc",
         "le_advertising_manager_test.cc",
         "le_scanning_manager_test.cc",
     ],
@@ -38,15 +43,13 @@
 filegroup {
     name: "BluetoothFacade_hci_layer",
     srcs: [
-        "facade.cc",
-        "facade/le_advertising_manager_facade.cc"
-    ],
-}
-
-filegroup {
-    name: "BluetoothCertSource_hci_layer",
-    srcs: [
-        "cert/cert.cc",
+        "facade/facade.cc",
+        "facade/acl_manager_facade.cc",
+        "facade/controller_facade.cc",
+        "facade/le_acl_manager_facade.cc",
+        "facade/le_advertising_manager_facade.cc",
+        "facade/le_initiator_address_facade.cc",
+        "facade/le_scanning_manager_facade.cc",
     ],
 }
 
@@ -56,3 +59,13 @@
         "hci_packets_fuzz_test.cc",
     ],
 }
+
+filegroup {
+    name: "BluetoothHciFuzzHelperSources",
+    srcs: [
+        "fuzz/status_vs_complete_commands.cc",
+        "fuzz/hci_layer_fuzz_client.cc",
+        "fuzz/fuzz_hci_layer.cc",
+    ],
+}
+
diff --git a/gd/hci/acl_connection_interface.h b/gd/hci/acl_connection_interface.h
new file mode 100644
index 0000000..e9e2201
--- /dev/null
+++ b/gd/hci/acl_connection_interface.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "hci/command_interface.h"
+#include "hci/hci_packets.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+constexpr EventCode AclConnectionEvents[] = {
+    EventCode::CONNECTION_PACKET_TYPE_CHANGED,
+    EventCode::ROLE_CHANGE,
+    EventCode::CONNECTION_COMPLETE,
+    EventCode::CONNECTION_REQUEST,
+    EventCode::AUTHENTICATION_COMPLETE,
+    EventCode::READ_CLOCK_OFFSET_COMPLETE,
+    EventCode::MODE_CHANGE,
+    EventCode::QOS_SETUP_COMPLETE,
+    EventCode::FLOW_SPECIFICATION_COMPLETE,
+    EventCode::FLUSH_OCCURRED,
+    EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE,
+    EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE,
+    EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE,
+    EventCode::LINK_SUPERVISION_TIMEOUT_CHANGED,
+};
+
+typedef CommandInterface<ConnectionManagementCommandBuilder> AclConnectionInterface;
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager.cc b/gd/hci/acl_manager.cc
index 4e337ff..a7cb8d96 100644
--- a/gd/hci/acl_manager.cc
+++ b/gd/hci/acl_manager.cc
@@ -16,47 +16,37 @@
 
 #include "hci/acl_manager.h"
 
-#include <future>
-#include <queue>
+#include <atomic>
 #include <set>
-#include <utility>
 
-#include "acl_fragmenter.h"
-#include "acl_manager.h"
 #include "common/bidi_queue.h"
+#include "hci/acl_manager/classic_impl.h"
+#include "hci/acl_manager/connection_management_callbacks.h"
+#include "hci/acl_manager/le_acl_connection.h"
+#include "hci/acl_manager/le_impl.h"
+#include "hci/acl_manager/round_robin_scheduler.h"
 #include "hci/controller.h"
 #include "hci/hci_layer.h"
+#include "security/security_module.h"
 
 namespace bluetooth {
 namespace hci {
 
 constexpr uint16_t kQualcommDebugHandle = 0xedc;
 
+using acl_manager::AclConnection;
 using common::Bind;
 using common::BindOnce;
 
-struct AclManager::acl_connection {
-  acl_connection(AddressWithType address_with_type) : address_with_type_(address_with_type) {}
-  friend AclConnection;
-  AddressWithType address_with_type_;
-  std::unique_ptr<AclConnection::Queue> queue_ = std::make_unique<AclConnection::Queue>(10);
-  bool is_disconnected_ = false;
-  ErrorCode disconnect_reason_;
-  os::Handler* command_complete_handler_ = nullptr;
-  os::Handler* disconnect_handler_ = nullptr;
-  ConnectionManagementCallbacks* command_complete_callbacks_;
-  common::OnceCallback<void(ErrorCode)> on_disconnect_callback_;
-  // For LE Connection parameter update from L2CAP
-  common::OnceCallback<void(ErrorCode)> on_connection_update_complete_callback_;
-  os::Handler* on_connection_update_complete_callback_handler_ = nullptr;
-  // Round-robin: Track if dequeue is registered for this connection
-  bool is_registered_ = false;
-  // Credits: Track the number of packets which have been sent to the controller
-  uint16_t number_of_sent_packets_ = 0;
-  void call_disconnect_callback() {
-    disconnect_handler_->Post(BindOnce(std::move(on_disconnect_callback_), disconnect_reason_));
-  }
-};
+using acl_manager::classic_impl;
+using acl_manager::ClassicAclConnection;
+using acl_manager::ConnectionCallbacks;
+
+using acl_manager::le_impl;
+using acl_manager::LeAclConnection;
+using acl_manager::LeConnectionCallbacks;
+
+using acl_manager::RoundRobinScheduler;
 
 struct AclManager::impl {
   impl(const AclManager& acl_manager) : acl_manager_(acl_manager) {}
@@ -65,162 +55,29 @@
     hci_layer_ = acl_manager_.GetDependency<HciLayer>();
     handler_ = acl_manager_.GetHandler();
     controller_ = acl_manager_.GetDependency<Controller>();
-    max_acl_packet_credits_ = controller_->GetControllerNumAclPacketBuffers();
-    acl_packet_credits_ = max_acl_packet_credits_;
-    acl_buffer_length_ = controller_->GetControllerAclPacketLength();
-    controller_->RegisterCompletedAclPacketsCallback(
-        common::Bind(&impl::incoming_acl_credits, common::Unretained(this)), handler_);
+    round_robin_scheduler_ = new RoundRobinScheduler(handler_, controller_, hci_layer_->GetAclQueueEnd());
 
-    // TODO: determine when we should reject connection
-    should_accept_connection_ = common::Bind([](Address, ClassOfDevice) { return true; });
     hci_queue_end_ = hci_layer_->GetAclQueueEnd();
     hci_queue_end_->RegisterDequeue(
         handler_, common::Bind(&impl::dequeue_and_route_acl_packet_to_connection, common::Unretained(this)));
-    hci_layer_->RegisterEventHandler(EventCode::CONNECTION_COMPLETE,
-                                     Bind(&impl::on_connection_complete, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::DISCONNECTION_COMPLETE,
-                                     Bind(&impl::on_disconnection_complete, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::CONNECTION_REQUEST,
-                                     Bind(&impl::on_incoming_connection, common::Unretained(this)), handler_);
-    hci_layer_->RegisterLeEventHandler(SubeventCode::CONNECTION_COMPLETE,
-                                       Bind(&impl::on_le_connection_complete, common::Unretained(this)), handler_);
-    hci_layer_->RegisterLeEventHandler(SubeventCode::ENHANCED_CONNECTION_COMPLETE,
-                                       Bind(&impl::on_le_enhanced_connection_complete, common::Unretained(this)),
-                                       handler_);
-    hci_layer_->RegisterLeEventHandler(SubeventCode::CONNECTION_UPDATE_COMPLETE,
-                                       Bind(&impl::on_le_connection_update_complete, common::Unretained(this)),
-                                       handler_);
-    hci_layer_->RegisterEventHandler(EventCode::CONNECTION_PACKET_TYPE_CHANGED,
-                                     Bind(&impl::on_connection_packet_type_changed, common::Unretained(this)),
-                                     handler_);
-    hci_layer_->RegisterEventHandler(EventCode::AUTHENTICATION_COMPLETE,
-                                     Bind(&impl::on_authentication_complete, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::READ_CLOCK_OFFSET_COMPLETE,
-                                     Bind(&impl::on_read_clock_offset_complete, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::MODE_CHANGE, Bind(&impl::on_mode_change, common::Unretained(this)),
-                                     handler_);
-    hci_layer_->RegisterEventHandler(EventCode::QOS_SETUP_COMPLETE,
-                                     Bind(&impl::on_qos_setup_complete, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::ROLE_CHANGE, Bind(&impl::on_role_change, common::Unretained(this)),
-                                     handler_);
-    hci_layer_->RegisterEventHandler(EventCode::FLOW_SPECIFICATION_COMPLETE,
-                                     Bind(&impl::on_flow_specification_complete, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::FLUSH_OCCURRED,
-                                     Bind(&impl::on_flush_occurred, common::Unretained(this)), handler_);
-    hci_mtu_ = controller_->GetControllerAclPacketLength();
+    classic_impl_ = new classic_impl(hci_layer_, controller_, handler_, round_robin_scheduler_);
+    le_impl_ = new le_impl(hci_layer_, controller_, handler_, round_robin_scheduler_, classic_impl_);
   }
 
   void Stop() {
-    hci_layer_->UnregisterEventHandler(EventCode::DISCONNECTION_COMPLETE);
-    hci_layer_->UnregisterEventHandler(EventCode::CONNECTION_COMPLETE);
-    hci_layer_->UnregisterEventHandler(EventCode::CONNECTION_REQUEST);
-    hci_layer_->UnregisterEventHandler(EventCode::AUTHENTICATION_COMPLETE);
+    delete le_impl_;
+    delete classic_impl_;
     hci_queue_end_->UnregisterDequeue();
-    unregister_all_connections();
-    acl_connections_.clear();
+    delete round_robin_scheduler_;
+    if (enqueue_registered_.exchange(false)) {
+      hci_queue_end_->UnregisterEnqueue();
+    }
     hci_queue_end_ = nullptr;
     handler_ = nullptr;
     hci_layer_ = nullptr;
   }
 
-  void incoming_acl_credits(uint16_t handle, uint16_t credits) {
-    auto connection_pair = acl_connections_.find(handle);
-    if (connection_pair == acl_connections_.end()) {
-      LOG_INFO("Dropping %hx received credits to unknown connection 0x%0hx", credits, handle);
-      return;
-    }
-    if (connection_pair->second.is_disconnected_) {
-      LOG_INFO("Dropping %hx received credits to disconnected connection 0x%0hx", credits, handle);
-      return;
-    }
-    connection_pair->second.number_of_sent_packets_ -= credits;
-    acl_packet_credits_ += credits;
-    ASSERT(acl_packet_credits_ <= max_acl_packet_credits_);
-    start_round_robin();
-  }
-
-  // Round-robin scheduler
-  void start_round_robin() {
-    if (acl_packet_credits_ == 0) {
-      return;
-    }
-    if (!fragments_to_send_.empty()) {
-      send_next_fragment();
-      return;
-    }
-    for (auto connection_pair = acl_connections_.begin(); connection_pair != acl_connections_.end();
-         connection_pair = std::next(connection_pair)) {
-      if (connection_pair->second.is_registered_) {
-        continue;
-      }
-      connection_pair->second.is_registered_ = true;
-      connection_pair->second.queue_->GetDownEnd()->RegisterDequeue(
-          handler_, common::Bind(&impl::handle_dequeue_from_upper, common::Unretained(this), connection_pair));
-    }
-  }
-
-  void handle_dequeue_from_upper(std::map<uint16_t, acl_connection>::iterator connection_pair) {
-    current_connection_pair_ = connection_pair;
-    buffer_packet();
-  }
-
-  void unregister_all_connections() {
-    for (auto connection_pair = acl_connections_.begin(); connection_pair != acl_connections_.end();
-         connection_pair = std::next(connection_pair)) {
-      if (connection_pair->second.is_registered_) {
-        connection_pair->second.is_registered_ = false;
-        connection_pair->second.queue_->GetDownEnd()->UnregisterDequeue();
-      }
-    }
-  }
-
-  void buffer_packet() {
-    unregister_all_connections();
-    BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
-    //   Wrap packet and enqueue it
-    uint16_t handle = current_connection_pair_->first;
-
-    auto packet = current_connection_pair_->second.queue_->GetDownEnd()->TryDequeue();
-    ASSERT(packet != nullptr);
-
-    if (packet->size() <= hci_mtu_) {
-      fragments_to_send_.push_front(AclPacketBuilder::Create(handle, PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
-                                                             broadcast_flag, std::move(packet)));
-    } else {
-      auto fragments = AclFragmenter(hci_mtu_, std::move(packet)).GetFragments();
-      PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
-      for (size_t i = 0; i < fragments.size(); i++) {
-        fragments_to_send_.push_back(
-            AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(fragments[i])));
-        packet_boundary_flag = PacketBoundaryFlag::CONTINUING_FRAGMENT;
-      }
-    }
-    ASSERT(fragments_to_send_.size() > 0);
-
-    current_connection_pair_->second.number_of_sent_packets_ += fragments_to_send_.size();
-    send_next_fragment();
-  }
-
-  void send_next_fragment() {
-    hci_queue_end_->RegisterEnqueue(handler_,
-                                    common::Bind(&impl::handle_enqueue_next_fragment, common::Unretained(this)));
-  }
-
-  std::unique_ptr<AclPacketBuilder> handle_enqueue_next_fragment() {
-    ASSERT(acl_packet_credits_ > 0);
-    if (acl_packet_credits_ == 1 || fragments_to_send_.size() == 1) {
-      hci_queue_end_->UnregisterEnqueue();
-      if (fragments_to_send_.size() == 1) {
-        handler_->Post(common::BindOnce(&impl::start_round_robin, common::Unretained(this)));
-      }
-    }
-    ASSERT(fragments_to_send_.size() > 0);
-    auto raw_pointer = fragments_to_send_.front().release();
-    acl_packet_credits_ -= 1;
-    fragments_to_send_.pop_front();
-    return std::unique_ptr<AclPacketBuilder>(raw_pointer);
-  }
-
+  // Invoked from some external Queue Reactable context 2
   void dequeue_and_route_acl_packet_to_connection() {
     auto packet = hci_queue_end_->TryDequeue();
     ASSERT(packet != nullptr);
@@ -232,1636 +89,120 @@
     if (handle == kQualcommDebugHandle) {
       return;
     }
-    auto connection_pair = acl_connections_.find(handle);
-    if (connection_pair == acl_connections_.end()) {
-      LOG_INFO("Dropping packet of size %zu to unknown connection 0x%0hx", packet->size(), handle);
-      return;
-    }
-    // TODO: What happens if the connection is stalled and fills up?
-    // TODO hsz: define enqueue callback
-    auto queue_end = connection_pair->second.queue_->GetDownEnd();
-    PacketView<kLittleEndian> payload = packet->GetPayload();
-    queue_end->RegisterEnqueue(handler_, common::Bind(
-                                             [](decltype(queue_end) queue_end, PacketView<kLittleEndian> payload) {
-                                               queue_end->UnregisterEnqueue();
-                                               return std::make_unique<PacketView<kLittleEndian>>(payload);
-                                             },
-                                             queue_end, std::move(payload)));
-  }
-
-  void on_incoming_connection(EventPacketView packet) {
-    ConnectionRequestView request = ConnectionRequestView::Create(packet);
-    ASSERT(request.IsValid());
-    Address address = request.GetBdAddr();
-    if (client_callbacks_ == nullptr) {
-      LOG_ERROR("No callbacks to call");
-      auto reason = RejectConnectionReason::LIMITED_RESOURCES;
-      this->reject_connection(RejectConnectionRequestBuilder::Create(address, reason));
-      return;
-    }
-    connecting_.insert(address);
-    if (is_classic_link_already_connected(address)) {
-      auto reason = RejectConnectionReason::UNACCEPTABLE_BD_ADDR;
-      this->reject_connection(RejectConnectionRequestBuilder::Create(address, reason));
-    } else if (should_accept_connection_.Run(address, request.GetClassOfDevice())) {
-      this->accept_connection(address);
+    auto connection_pair = classic_impl_->acl_connections_.find(handle);
+    if (connection_pair != classic_impl_->acl_connections_.end()) {
+      connection_pair->second.assembler_.on_incoming_packet(*packet);
     } else {
-      auto reason = RejectConnectionReason::LIMITED_RESOURCES;  // TODO: determine reason
-      this->reject_connection(RejectConnectionRequestBuilder::Create(address, reason));
-    }
-  }
-
-  void on_classic_connection_complete(Address address) {
-    auto connecting_addr = connecting_.find(address);
-    if (connecting_addr == connecting_.end()) {
-      LOG_WARN("No prior connection request for %s", address.ToString().c_str());
-    } else {
-      connecting_.erase(connecting_addr);
-    }
-  }
-
-  void on_common_le_connection_complete(AddressWithType address_with_type) {
-    auto connecting_addr_with_type = connecting_le_.find(address_with_type);
-    if (connecting_addr_with_type == connecting_le_.end()) {
-      LOG_WARN("No prior connection request for %s", address_with_type.ToString().c_str());
-    } else {
-      connecting_le_.erase(connecting_addr_with_type);
-    }
-  }
-
-  void on_le_connection_complete(LeMetaEventView packet) {
-    LeConnectionCompleteView connection_complete = LeConnectionCompleteView::Create(packet);
-    ASSERT(connection_complete.IsValid());
-    auto status = connection_complete.GetStatus();
-    auto address = connection_complete.GetPeerAddress();
-    auto peer_address_type = connection_complete.GetPeerAddressType();
-    // TODO: find out which address and type was used to initiate the connection
-    AddressWithType address_with_type(address, peer_address_type);
-    on_common_le_connection_complete(address_with_type);
-    if (status != ErrorCode::SUCCESS) {
-      le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectFail,
-                                                common::Unretained(le_client_callbacks_), address_with_type, status));
-      return;
-    }
-    // TODO: Check and save other connection parameters
-    uint16_t handle = connection_complete.GetConnectionHandle();
-    ASSERT(acl_connections_.count(handle) == 0);
-    acl_connections_.emplace(handle, address_with_type);
-    if (acl_connections_.size() == 1 && fragments_to_send_.size() == 0) {
-      start_round_robin();
-    }
-    auto role = connection_complete.GetRole();
-    std::unique_ptr<AclConnection> connection_proxy(
-        new AclConnection(&acl_manager_, handle, address, peer_address_type, role));
-    le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
-                                              common::Unretained(le_client_callbacks_), address_with_type,
-                                              std::move(connection_proxy)));
-  }
-
-  void on_le_enhanced_connection_complete(LeMetaEventView packet) {
-    LeEnhancedConnectionCompleteView connection_complete = LeEnhancedConnectionCompleteView::Create(packet);
-    ASSERT(connection_complete.IsValid());
-    auto status = connection_complete.GetStatus();
-    auto address = connection_complete.GetPeerAddress();
-    auto peer_address_type = connection_complete.GetPeerAddressType();
-    auto peer_resolvable_address = connection_complete.GetPeerResolvablePrivateAddress();
-    AddressWithType reporting_address_with_type(address, peer_address_type);
-    if (!peer_resolvable_address.IsEmpty()) {
-      reporting_address_with_type = AddressWithType(peer_resolvable_address, AddressType::RANDOM_DEVICE_ADDRESS);
-    }
-    on_common_le_connection_complete(reporting_address_with_type);
-    if (status != ErrorCode::SUCCESS) {
-      le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectFail,
-                                                common::Unretained(le_client_callbacks_), reporting_address_with_type,
-                                                status));
-      return;
-    }
-    // TODO: Check and save other connection parameters
-    uint16_t handle = connection_complete.GetConnectionHandle();
-    ASSERT(acl_connections_.count(handle) == 0);
-    acl_connections_.emplace(handle, reporting_address_with_type);
-    if (acl_connections_.size() == 1 && fragments_to_send_.size() == 0) {
-      start_round_robin();
-    }
-    auto role = connection_complete.GetRole();
-    std::unique_ptr<AclConnection> connection_proxy(
-        new AclConnection(&acl_manager_, handle, address, peer_address_type, role));
-    le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
-                                              common::Unretained(le_client_callbacks_), reporting_address_with_type,
-                                              std::move(connection_proxy)));
-  }
-
-  void on_connection_complete(EventPacketView packet) {
-    ConnectionCompleteView connection_complete = ConnectionCompleteView::Create(packet);
-    ASSERT(connection_complete.IsValid());
-    auto status = connection_complete.GetStatus();
-    auto address = connection_complete.GetBdAddr();
-    on_classic_connection_complete(address);
-    if (status != ErrorCode::SUCCESS) {
-      client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectFail, common::Unretained(client_callbacks_),
-                                             address, status));
-      return;
-    }
-    uint16_t handle = connection_complete.GetConnectionHandle();
-    ASSERT(acl_connections_.count(handle) == 0);
-    acl_connections_.emplace(handle, AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS});
-    if (acl_connections_.size() == 1 && fragments_to_send_.size() == 0) {
-      start_round_robin();
-    }
-    std::unique_ptr<AclConnection> connection_proxy(new AclConnection(&acl_manager_, handle, address));
-    client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectSuccess,
-                                           common::Unretained(client_callbacks_), std::move(connection_proxy)));
-    while (!pending_outgoing_connections_.empty()) {
-      auto create_connection_packet_and_address = std::move(pending_outgoing_connections_.front());
-      pending_outgoing_connections_.pop();
-      if (!is_classic_link_already_connected(create_connection_packet_and_address.first)) {
-        connecting_.insert(create_connection_packet_and_address.first);
-        hci_layer_->EnqueueCommand(std::move(create_connection_packet_and_address.second),
-                                   common::BindOnce([](CommandStatusView status) {
-                                     ASSERT(status.IsValid());
-                                     ASSERT(status.GetCommandOpCode() == OpCode::CREATE_CONNECTION);
-                                   }),
-                                   handler_);
-        break;
-      }
-    }
-  }
-
-  void on_disconnection_complete(EventPacketView packet) {
-    DisconnectionCompleteView disconnection_complete = DisconnectionCompleteView::Create(packet);
-    ASSERT(disconnection_complete.IsValid());
-    uint16_t handle = disconnection_complete.GetConnectionHandle();
-    auto status = disconnection_complete.GetStatus();
-    if (status == ErrorCode::SUCCESS) {
-      ASSERT(acl_connections_.count(handle) == 1);
-      auto& acl_connection = acl_connections_.find(handle)->second;
-      acl_connection.is_disconnected_ = true;
-      acl_connection.disconnect_reason_ = disconnection_complete.GetReason();
-      acl_connection.call_disconnect_callback();
-      // Reclaim outstanding packets
-      acl_packet_credits_ += acl_connection.number_of_sent_packets_;
-      acl_connection.number_of_sent_packets_ = 0;
-    } else {
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received disconnection complete with error code %s, handle 0x%02hx", error_code.c_str(), handle);
-    }
-  }
-
-  void on_connection_packet_type_changed(EventPacketView packet) {
-    ConnectionPacketTypeChangedView packet_type_changed = ConnectionPacketTypeChangedView::Create(packet);
-    if (!packet_type_changed.IsValid()) {
-      LOG_ERROR("Received on_connection_packet_type_changed with invalid packet");
-      return;
-    } else if (packet_type_changed.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = packet_type_changed.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_connection_packet_type_changed with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = packet_type_changed.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint16_t packet_type = packet_type_changed.GetPacketType();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnConnectionPacketTypeChanged,
-                           common::Unretained(acl_connection.command_complete_callbacks_), packet_type));
-    }
-  }
-
-  void on_master_link_key_complete(EventPacketView packet) {
-    MasterLinkKeyCompleteView complete_view = MasterLinkKeyCompleteView::Create(packet);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_master_link_key_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_master_link_key_complete with error code %s", error_code.c_str());
-      return;
-    }
-    if (acl_manager_client_callbacks_ != nullptr) {
-      uint16_t connection_handle = complete_view.GetConnectionHandle();
-      KeyFlag key_flag = complete_view.GetKeyFlag();
-      acl_manager_client_handler_->Post(common::BindOnce(&AclManagerCallbacks::OnMasterLinkKeyComplete,
-                                                         common::Unretained(acl_manager_client_callbacks_),
-                                                         connection_handle, key_flag));
-    }
-  }
-
-  void on_authentication_complete(EventPacketView packet) {
-    AuthenticationCompleteView authentication_complete = AuthenticationCompleteView::Create(packet);
-    if (!authentication_complete.IsValid()) {
-      LOG_ERROR("Received on_authentication_complete with invalid packet");
-      return;
-    } else if (authentication_complete.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = authentication_complete.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_authentication_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = authentication_complete.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnAuthenticationComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_)));
-    }
-  }
-
-  void on_encryption_change(EventPacketView packet) {
-    EncryptionChangeView encryption_change_view = EncryptionChangeView::Create(packet);
-    if (!encryption_change_view.IsValid()) {
-      LOG_ERROR("Received on_encryption_change with invalid packet");
-      return;
-    } else if (encryption_change_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = encryption_change_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_change_connection_link_key_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = encryption_change_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      EncryptionEnabled enabled = encryption_change_view.GetEncryptionEnabled();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnEncryptionChange,
-                           common::Unretained(acl_connection.command_complete_callbacks_), enabled));
-    }
-  }
-
-  void on_change_connection_link_key_complete(EventPacketView packet) {
-    ChangeConnectionLinkKeyCompleteView complete_view = ChangeConnectionLinkKeyCompleteView::Create(packet);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_change_connection_link_key_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_change_connection_link_key_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnChangeConnectionLinkKeyComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_)));
-    }
-  }
-
-  void on_read_clock_offset_complete(EventPacketView packet) {
-    ReadClockOffsetCompleteView complete_view = ReadClockOffsetCompleteView::Create(packet);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_clock_offset_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_clock_offset_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint16_t clock_offset = complete_view.GetClockOffset();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadClockOffsetComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), clock_offset));
-    }
-  }
-
-  void on_mode_change(EventPacketView packet) {
-    ModeChangeView mode_change_view = ModeChangeView::Create(packet);
-    if (!mode_change_view.IsValid()) {
-      LOG_ERROR("Received on_mode_change with invalid packet");
-      return;
-    } else if (mode_change_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = mode_change_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_mode_change with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = mode_change_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      Mode current_mode = mode_change_view.GetCurrentMode();
-      uint16_t interval = mode_change_view.GetInterval();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnModeChange,
-                           common::Unretained(acl_connection.command_complete_callbacks_), current_mode, interval));
-    }
-  }
-
-  void on_qos_setup_complete(EventPacketView packet) {
-    QosSetupCompleteView complete_view = QosSetupCompleteView::Create(packet);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_qos_setup_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_qos_setup_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      ServiceType service_type = complete_view.GetServiceType();
-      uint32_t token_rate = complete_view.GetTokenRate();
-      uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
-      uint32_t latency = complete_view.GetLatency();
-      uint32_t delay_variation = complete_view.GetDelayVariation();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnQosSetupComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), service_type, token_rate,
-                           peak_bandwidth, latency, delay_variation));
-    }
-  }
-
-  void on_role_change(EventPacketView packet) {
-    RoleChangeView role_change_view = RoleChangeView::Create(packet);
-    if (!role_change_view.IsValid()) {
-      LOG_ERROR("Received on_role_change with invalid packet");
-      return;
-    } else if (role_change_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = role_change_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_role_change with error code %s", error_code.c_str());
-      return;
-    }
-    if (acl_manager_client_callbacks_ != nullptr) {
-      Address bd_addr = role_change_view.GetBdAddr();
-      Role new_role = role_change_view.GetNewRole();
-      acl_manager_client_handler_->Post(common::BindOnce(
-          &AclManagerCallbacks::OnRoleChange, common::Unretained(acl_manager_client_callbacks_), bd_addr, new_role));
-    }
-  }
-
-  void on_flow_specification_complete(EventPacketView packet) {
-    FlowSpecificationCompleteView complete_view = FlowSpecificationCompleteView::Create(packet);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_flow_specification_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_flow_specification_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      FlowDirection flow_direction = complete_view.GetFlowDirection();
-      ServiceType service_type = complete_view.GetServiceType();
-      uint32_t token_rate = complete_view.GetTokenRate();
-      uint32_t token_bucket_size = complete_view.GetTokenBucketSize();
-      uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
-      uint32_t access_latency = complete_view.GetAccessLatency();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnFlowSpecificationComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), flow_direction, service_type,
-                           token_rate, token_bucket_size, peak_bandwidth, access_latency));
-    }
-  }
-
-  void on_flush_occurred(EventPacketView packet) {
-    FlushOccurredView flush_occurred_view = FlushOccurredView::Create(packet);
-    if (!flush_occurred_view.IsValid()) {
-      LOG_ERROR("Received on_flush_occurred with invalid packet");
-      return;
-    }
-    uint16_t handle = flush_occurred_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnFlushOccurred,
-                           common::Unretained(acl_connection.command_complete_callbacks_)));
-    }
-  }
-
-  void on_role_discovery_complete(CommandCompleteView view) {
-    auto complete_view = RoleDiscoveryCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_role_discovery_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_role_discovery_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      Role role = complete_view.GetCurrentRole();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnRoleDiscoveryComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), role));
-    }
-  }
-
-  void on_read_link_policy_settings_complete(CommandCompleteView view) {
-    auto complete_view = ReadLinkPolicySettingsCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_link_policy_settings_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_link_policy_settings_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint16_t link_policy_settings = complete_view.GetLinkPolicySettings();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadLinkPolicySettingsComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), link_policy_settings));
-    }
-  }
-
-  void on_read_default_link_policy_settings_complete(CommandCompleteView view) {
-    auto complete_view = ReadDefaultLinkPolicySettingsCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_link_policy_settings_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_link_policy_settings_complete with error code %s", error_code.c_str());
-      return;
-    }
-    if (acl_manager_client_callbacks_ != nullptr) {
-      uint16_t default_link_policy_settings = complete_view.GetDefaultLinkPolicySettings();
-      acl_manager_client_handler_->Post(common::BindOnce(&AclManagerCallbacks::OnReadDefaultLinkPolicySettingsComplete,
-                                                         common::Unretained(acl_manager_client_callbacks_),
-                                                         default_link_policy_settings));
-    }
-  }
-
-  void on_read_automatic_flush_timeout_complete(CommandCompleteView view) {
-    auto complete_view = ReadAutomaticFlushTimeoutCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_automatic_flush_timeout_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_automatic_flush_timeout_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint16_t flush_timeout = complete_view.GetFlushTimeout();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadAutomaticFlushTimeoutComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), flush_timeout));
-    }
-  }
-
-  void on_read_transmit_power_level_complete(CommandCompleteView view) {
-    auto complete_view = ReadTransmitPowerLevelCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_transmit_power_level_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_transmit_power_level_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint8_t transmit_power_level = complete_view.GetTransmitPowerLevel();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadTransmitPowerLevelComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), transmit_power_level));
-    }
-  }
-
-  void on_read_link_supervision_timeout_complete(CommandCompleteView view) {
-    auto complete_view = ReadLinkSupervisionTimeoutCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_link_supervision_timeout_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_link_supervision_timeout_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint16_t link_supervision_timeout = complete_view.GetLinkSupervisionTimeout();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadLinkSupervisionTimeoutComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), link_supervision_timeout));
-    }
-  }
-
-  void on_read_failed_contact_counter_complete(CommandCompleteView view) {
-    auto complete_view = ReadFailedContactCounterCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_failed_contact_counter_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_failed_contact_counter_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint16_t failed_contact_counter = complete_view.GetFailedContactCounter();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadFailedContactCounterComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), failed_contact_counter));
-    }
-  }
-
-  void on_read_link_quality_complete(CommandCompleteView view) {
-    auto complete_view = ReadLinkQualityCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_link_quality_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_link_quality_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint8_t link_quality = complete_view.GetLinkQuality();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadLinkQualityComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), link_quality));
-    }
-  }
-
-  void on_read_afh_channel_map_complete(CommandCompleteView view) {
-    auto complete_view = ReadAfhChannelMapCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_afh_channel_map_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_afh_channel_map_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      AfhMode afh_mode = complete_view.GetAfhMode();
-      std::array<uint8_t, 10> afh_channel_map = complete_view.GetAfhChannelMap();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadAfhChannelMapComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), afh_mode, afh_channel_map));
-    }
-  }
-
-  void on_read_rssi_complete(CommandCompleteView view) {
-    auto complete_view = ReadRssiCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_rssi_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_rssi_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint8_t rssi = complete_view.GetRssi();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadRssiComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), rssi));
-    }
-  }
-
-  void on_read_clock_complete(CommandCompleteView view) {
-    auto complete_view = ReadClockCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_read_clock_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_read_clock_complete with error code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = complete_view.GetConnectionHandle();
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.command_complete_handler_ != nullptr) {
-      uint32_t clock = complete_view.GetClock();
-      uint16_t accuracy = complete_view.GetAccuracy();
-      acl_connection.command_complete_handler_->Post(
-          common::BindOnce(&ConnectionManagementCallbacks::OnReadClockComplete,
-                           common::Unretained(acl_connection.command_complete_callbacks_), clock, accuracy));
-    }
-  }
-
-  void on_le_connection_update_complete(LeMetaEventView view) {
-    auto complete_view = LeConnectionUpdateCompleteView::Create(view);
-    if (!complete_view.IsValid()) {
-      LOG_ERROR("Received on_le_connection_update_complete with invalid packet");
-      return;
-    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = complete_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received on_le_connection_update_complete with error code %s", error_code.c_str());
-      return;
-    }
-    auto handle = complete_view.GetConnectionHandle();
-    if (acl_connections_.find(handle) == acl_connections_.end()) {
-      LOG_WARN("Can't find connection");
-      return;
-    }
-    auto& connection = acl_connections_.find(handle)->second;
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return;
-    }
-    if (!connection.on_connection_update_complete_callback_.is_null()) {
-      connection.on_connection_update_complete_callback_handler_->Post(
-          common::BindOnce(std::move(connection.on_connection_update_complete_callback_), complete_view.GetStatus()));
-      connection.on_connection_update_complete_callback_handler_ = nullptr;
-    }
-  }
-
-  bool is_classic_link_already_connected(Address address) {
-    for (const auto& connection : acl_connections_) {
-      if (connection.second.address_with_type_.GetAddress() == address) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  void create_connection(Address address) {
-    // TODO: Configure default connection parameters?
-    uint16_t packet_type = 0x4408 /* DM 1,3,5 */ | 0x8810 /*DH 1,3,5 */;
-    PageScanRepetitionMode page_scan_repetition_mode = PageScanRepetitionMode::R1;
-    uint16_t clock_offset = 0;
-    ClockOffsetValid clock_offset_valid = ClockOffsetValid::INVALID;
-    CreateConnectionRoleSwitch allow_role_switch = CreateConnectionRoleSwitch::ALLOW_ROLE_SWITCH;
-    ASSERT(client_callbacks_ != nullptr);
-    std::unique_ptr<CreateConnectionBuilder> packet = CreateConnectionBuilder::Create(
-        address, packet_type, page_scan_repetition_mode, clock_offset, clock_offset_valid, allow_role_switch);
-
-    if (connecting_.empty()) {
-      if (is_classic_link_already_connected(address)) {
-        LOG_WARN("already connected: %s", address.ToString().c_str());
+      auto le_connection_pair = le_impl_->le_acl_connections_.find(handle);
+      if (le_connection_pair == le_impl_->le_acl_connections_.end()) {
+        LOG_INFO("Dropping packet of size %zu to unknown connection 0x%0hx", packet->size(), handle);
         return;
       }
-      connecting_.insert(address);
-      hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandStatusView status) {
-                                   ASSERT(status.IsValid());
-                                   ASSERT(status.GetCommandOpCode() == OpCode::CREATE_CONNECTION);
-                                 }),
-                                 handler_);
-    } else {
-      pending_outgoing_connections_.emplace(address, std::move(packet));
+      le_connection_pair->second.assembler_.on_incoming_packet(*packet);
     }
   }
 
-  void create_le_connection(AddressWithType address_with_type) {
-    // TODO: Add white list handling.
-    // TODO: Configure default LE connection parameters?
-    uint16_t le_scan_interval = 0x0020;
-    uint16_t le_scan_window = 0x0010;
-    InitiatorFilterPolicy initiator_filter_policy = InitiatorFilterPolicy::USE_PEER_ADDRESS;
-    OwnAddressType own_address_type = OwnAddressType::RANDOM_DEVICE_ADDRESS;
-    uint16_t conn_interval_min = 0x0006;
-    uint16_t conn_interval_max = 0x0C00;
-    uint16_t conn_latency = 0x0C0;
-    uint16_t supervision_timeout = 0x0C00;
-    ASSERT(le_client_callbacks_ != nullptr);
-
-    connecting_le_.insert(address_with_type);
-
-    hci_layer_->EnqueueCommand(
-        LeCreateConnectionBuilder::Create(le_scan_interval, le_scan_window, initiator_filter_policy,
-                                          address_with_type.GetAddressType(), address_with_type.GetAddress(),
-                                          own_address_type, conn_interval_min, conn_interval_max, conn_latency,
-                                          supervision_timeout, kMinimumCeLength, kMaximumCeLength),
-        common::BindOnce([](CommandStatusView status) {
-          ASSERT(status.IsValid());
-          ASSERT(status.GetCommandOpCode() == OpCode::LE_CREATE_CONNECTION);
-        }),
-        handler_);
-  }
-
-  void cancel_connect(Address address) {
-    auto connecting_addr = connecting_.find(address);
-    if (connecting_addr == connecting_.end()) {
-      LOG_INFO("Cannot cancel non-existent connection to %s", address.ToString().c_str());
-      return;
-    }
-    std::unique_ptr<CreateConnectionCancelBuilder> packet = CreateConnectionCancelBuilder::Create(address);
-    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandCompleteView complete) { /* TODO */ }),
-                               handler_);
-  }
-
-  void master_link_key(KeyFlag key_flag) {
-    std::unique_ptr<MasterLinkKeyBuilder> packet = MasterLinkKeyBuilder::Create(key_flag);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        common::BindOnce(&impl::check_command_status<MasterLinkKeyStatusView>, common::Unretained(this)), handler_);
-  }
-
-  void switch_role(Address address, Role role) {
-    std::unique_ptr<SwitchRoleBuilder> packet = SwitchRoleBuilder::Create(address, role);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        common::BindOnce(&impl::check_command_status<SwitchRoleStatusView>, common::Unretained(this)), handler_);
-  }
-
-  void read_default_link_policy_settings() {
-    std::unique_ptr<ReadDefaultLinkPolicySettingsBuilder> packet = ReadDefaultLinkPolicySettingsBuilder::Create();
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        common::BindOnce(&impl::on_read_default_link_policy_settings_complete, common::Unretained(this)), handler_);
-  }
-
-  void write_default_link_policy_settings(uint16_t default_link_policy_settings) {
-    std::unique_ptr<WriteDefaultLinkPolicySettingsBuilder> packet =
-        WriteDefaultLinkPolicySettingsBuilder::Create(default_link_policy_settings);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_complete<WriteDefaultLinkPolicySettingsCompleteView>,
-                 common::Unretained(this)),
-        handler_);
-  }
-
-  void accept_connection(Address address) {
-    auto role = AcceptConnectionRequestRole::BECOME_MASTER;  // We prefer to be master
-    hci_layer_->EnqueueCommand(AcceptConnectionRequestBuilder::Create(address, role),
-                               common::BindOnce(&impl::on_accept_connection_status, common::Unretained(this), address),
-                               handler_);
-  }
-
-  void handle_disconnect(uint16_t handle, DisconnectReason reason) {
-    ASSERT(acl_connections_.count(handle) == 1);
-    std::unique_ptr<DisconnectBuilder> packet = DisconnectBuilder::Create(handle, reason);
-    hci_layer_->EnqueueCommand(std::move(packet), BindOnce([](CommandStatusView status) { /* TODO: check? */ }),
-                               handler_);
-  }
-
-  void handle_change_connection_packet_type(uint16_t handle, uint16_t packet_type) {
-    ASSERT(acl_connections_.count(handle) == 1);
-    std::unique_ptr<ChangeConnectionPacketTypeBuilder> packet =
-        ChangeConnectionPacketTypeBuilder::Create(handle, packet_type);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               BindOnce(&AclManager::impl::check_command_status<ChangeConnectionPacketTypeStatusView>,
-                                        common::Unretained(this)),
-                               handler_);
-  }
-
-  void handle_authentication_requested(uint16_t handle) {
-    std::unique_ptr<AuthenticationRequestedBuilder> packet = AuthenticationRequestedBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_status<AuthenticationRequestedStatusView>, common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_set_connection_encryption(uint16_t handle, Enable enable) {
-    std::unique_ptr<SetConnectionEncryptionBuilder> packet = SetConnectionEncryptionBuilder::Create(handle, enable);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_status<SetConnectionEncryptionStatusView>, common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_change_connection_link_key(uint16_t handle) {
-    std::unique_ptr<ChangeConnectionLinkKeyBuilder> packet = ChangeConnectionLinkKeyBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_status<ChangeConnectionLinkKeyStatusView>, common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_read_clock_offset(uint16_t handle) {
-    std::unique_ptr<ReadClockOffsetBuilder> packet = ReadClockOffsetBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_status<ReadClockOffsetStatusView>, common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_hold_mode(uint16_t handle, uint16_t max_interval, uint16_t min_interval) {
-    std::unique_ptr<HoldModeBuilder> packet = HoldModeBuilder::Create(handle, max_interval, min_interval);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_status<HoldModeStatusView>, common::Unretained(this)), handler_);
-  }
-
-  void handle_sniff_mode(uint16_t handle, uint16_t max_interval, uint16_t min_interval, int16_t attempt,
-                         uint16_t timeout) {
-    std::unique_ptr<SniffModeBuilder> packet =
-        SniffModeBuilder::Create(handle, max_interval, min_interval, attempt, timeout);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_status<SniffModeStatusView>, common::Unretained(this)), handler_);
-  }
-
-  void handle_exit_sniff_mode(uint16_t handle) {
-    std::unique_ptr<ExitSniffModeBuilder> packet = ExitSniffModeBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_status<ExitSniffModeStatusView>, common::Unretained(this)), handler_);
-  }
-
-  void handle_qos_setup_mode(uint16_t handle, ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
-                             uint32_t latency, uint32_t delay_variation) {
-    std::unique_ptr<QosSetupBuilder> packet =
-        QosSetupBuilder::Create(handle, service_type, token_rate, peak_bandwidth, latency, delay_variation);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_status<QosSetupStatusView>, common::Unretained(this)), handler_);
-  }
-
-  void handle_role_discovery(uint16_t handle) {
-    std::unique_ptr<RoleDiscoveryBuilder> packet = RoleDiscoveryBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_role_discovery_complete, common::Unretained(this)), handler_);
-  }
-
-  void handle_read_link_policy_settings(uint16_t handle) {
-    std::unique_ptr<ReadLinkPolicySettingsBuilder> packet = ReadLinkPolicySettingsBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_read_link_policy_settings_complete, common::Unretained(this)),
-                               handler_);
-  }
-
-  void handle_write_link_policy_settings(uint16_t handle, uint16_t link_policy_settings) {
-    std::unique_ptr<WriteLinkPolicySettingsBuilder> packet =
-        WriteLinkPolicySettingsBuilder::Create(handle, link_policy_settings);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               BindOnce(&AclManager::impl::check_command_complete<WriteLinkPolicySettingsCompleteView>,
-                                        common::Unretained(this)),
-                               handler_);
-  }
-
-  void handle_flow_specification(uint16_t handle, FlowDirection flow_direction, ServiceType service_type,
-                                 uint32_t token_rate, uint32_t token_bucket_size, uint32_t peak_bandwidth,
-                                 uint32_t access_latency) {
-    std::unique_ptr<FlowSpecificationBuilder> packet = FlowSpecificationBuilder::Create(
-        handle, flow_direction, service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_status<FlowSpecificationStatusView>, common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_sniff_subrating(uint16_t handle, uint16_t maximum_latency, uint16_t minimum_remote_timeout,
-                              uint16_t minimum_local_timeout) {
-    std::unique_ptr<SniffSubratingBuilder> packet =
-        SniffSubratingBuilder::Create(handle, maximum_latency, minimum_remote_timeout, minimum_local_timeout);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_complete<SniffSubratingCompleteView>, common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_flush(uint16_t handle) {
-    std::unique_ptr<FlushBuilder> packet = FlushBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_complete<FlushCompleteView>, common::Unretained(this)), handler_);
-  }
-
-  void handle_read_automatic_flush_timeout(uint16_t handle) {
-    std::unique_ptr<ReadAutomaticFlushTimeoutBuilder> packet = ReadAutomaticFlushTimeoutBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(
-        std::move(packet), common::BindOnce(&impl::on_read_automatic_flush_timeout_complete, common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_write_automatic_flush_timeout(uint16_t handle, uint16_t flush_timeout) {
-    std::unique_ptr<WriteAutomaticFlushTimeoutBuilder> packet =
-        WriteAutomaticFlushTimeoutBuilder::Create(handle, flush_timeout);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_complete<WriteAutomaticFlushTimeoutCompleteView>,
-                 common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_read_transmit_power_level(uint16_t handle, TransmitPowerLevelType type) {
-    std::unique_ptr<ReadTransmitPowerLevelBuilder> packet = ReadTransmitPowerLevelBuilder::Create(handle, type);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_read_transmit_power_level_complete, common::Unretained(this)),
-                               handler_);
-  }
-
-  void handle_read_link_supervision_timeout(uint16_t handle) {
-    std::unique_ptr<ReadLinkSupervisionTimeoutBuilder> packet = ReadLinkSupervisionTimeoutBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(
-        std::move(packet), common::BindOnce(&impl::on_read_link_supervision_timeout_complete, common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_write_link_supervision_timeout(uint16_t handle, uint16_t link_supervision_timeout) {
-    std::unique_ptr<WriteLinkSupervisionTimeoutBuilder> packet =
-        WriteLinkSupervisionTimeoutBuilder::Create(handle, link_supervision_timeout);
-    hci_layer_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&AclManager::impl::check_command_complete<WriteLinkSupervisionTimeoutCompleteView>,
-                 common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_read_failed_contact_counter(uint16_t handle) {
-    std::unique_ptr<ReadFailedContactCounterBuilder> packet = ReadFailedContactCounterBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(
-        std::move(packet), common::BindOnce(&impl::on_read_failed_contact_counter_complete, common::Unretained(this)),
-        handler_);
-  }
-
-  void handle_reset_failed_contact_counter(uint16_t handle) {
-    std::unique_ptr<ResetFailedContactCounterBuilder> packet = ResetFailedContactCounterBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(std::move(packet), BindOnce([](CommandCompleteView view) { /* TODO: check? */ }),
-                               handler_);
-  }
-
-  void handle_read_link_quality(uint16_t handle) {
-    std::unique_ptr<ReadLinkQualityBuilder> packet = ReadLinkQualityBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(
-        std::move(packet), common::BindOnce(&impl::on_read_link_quality_complete, common::Unretained(this)), handler_);
-  }
-
-  void handle_afh_channel_map(uint16_t handle) {
-    std::unique_ptr<ReadAfhChannelMapBuilder> packet = ReadAfhChannelMapBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_read_afh_channel_map_complete, common::Unretained(this)),
-                               handler_);
-  }
-
-  void handle_read_rssi(uint16_t handle) {
-    std::unique_ptr<ReadRssiBuilder> packet = ReadRssiBuilder::Create(handle);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_read_rssi_complete, common::Unretained(this)), handler_);
-  }
-
-  void handle_read_clock(uint16_t handle, WhichClock which_clock) {
-    std::unique_ptr<ReadClockBuilder> packet = ReadClockBuilder::Create(handle, which_clock);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_read_clock_complete, common::Unretained(this)), handler_);
-  }
-
-  void handle_le_connection_update(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max,
-                                   uint16_t conn_latency, uint16_t supervision_timeout) {
-    auto packet = LeConnectionUpdateBuilder::Create(handle, conn_interval_min, conn_interval_max, conn_latency,
-                                                    supervision_timeout, kMinimumCeLength, kMaximumCeLength);
-    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandStatusView status) {
-                                 ASSERT(status.IsValid());
-                                 ASSERT(status.GetCommandOpCode() == OpCode::LE_CREATE_CONNECTION);
-                               }),
-                               handler_);
-  }
-
-  template <class T>
-  void check_command_complete(CommandCompleteView view) {
-    ASSERT(view.IsValid());
-    auto status_view = T::Create(view);
-    if (!status_view.IsValid()) {
-      LOG_ERROR("Received command complete with invalid packet, opcode 0x%02hx", view.GetCommandOpCode());
-      return;
-    }
-    ErrorCode status = status_view.GetStatus();
-    OpCode op_code = status_view.GetCommandOpCode();
-    if (status != ErrorCode::SUCCESS) {
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received command complete with error code %s, opcode 0x%02hx", error_code.c_str(), op_code);
-      return;
-    }
-  }
-
-  template <class T>
-  void check_command_status(CommandStatusView view) {
-    ASSERT(view.IsValid());
-    auto status_view = T::Create(view);
-    if (!status_view.IsValid()) {
-      LOG_ERROR("Received command status with invalid packet, opcode 0x%02hx", view.GetCommandOpCode());
-      return;
-    }
-    ErrorCode status = status_view.GetStatus();
-    OpCode op_code = status_view.GetCommandOpCode();
-    if (status != ErrorCode::SUCCESS) {
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("Received command status with error code %s, opcode 0x%02hx", error_code.c_str(), op_code);
-      return;
-    }
-  }
-
-  void cleanup(uint16_t handle) {
-    ASSERT(acl_connections_.count(handle) == 1);
-    auto& acl_connection = acl_connections_.find(handle)->second;
-    if (acl_connection.is_registered_) {
-      acl_connection.is_registered_ = false;
-      acl_connection.queue_->GetDownEnd()->UnregisterDequeue();
-    }
-    acl_connections_.erase(handle);
-  }
-
-  void on_accept_connection_status(Address address, CommandStatusView status) {
-    auto accept_status = AcceptConnectionRequestStatusView::Create(status);
-    ASSERT(accept_status.IsValid());
-    if (status.GetStatus() != ErrorCode::SUCCESS) {
-      cancel_connect(address);
-    }
-  }
-
-  void reject_connection(std::unique_ptr<RejectConnectionRequestBuilder> builder) {
-    hci_layer_->EnqueueCommand(std::move(builder), BindOnce([](CommandStatusView status) { /* TODO: check? */ }),
-                               handler_);
-  }
-
-  void handle_register_callbacks(ConnectionCallbacks* callbacks, os::Handler* handler) {
-    ASSERT(client_callbacks_ == nullptr);
-    ASSERT(client_handler_ == nullptr);
-    client_callbacks_ = callbacks;
-    client_handler_ = handler;
-  }
-
-  void handle_register_le_callbacks(LeConnectionCallbacks* callbacks, os::Handler* handler) {
-    ASSERT(le_client_callbacks_ == nullptr);
-    ASSERT(le_client_handler_ == nullptr);
-    le_client_callbacks_ = callbacks;
-    le_client_handler_ = handler;
-  }
-
-  void handle_register_acl_manager_callbacks(AclManagerCallbacks* callbacks, os::Handler* handler) {
-    ASSERT(acl_manager_client_callbacks_ == nullptr);
-    ASSERT(acl_manager_client_handler_ == nullptr);
-    acl_manager_client_callbacks_ = callbacks;
-    acl_manager_client_handler_ = handler;
-  }
-
-  acl_connection& check_and_get_connection(uint16_t handle) {
-    auto connection = acl_connections_.find(handle);
-    ASSERT(connection != acl_connections_.end());
-    return connection->second;
-  }
-
-  AclConnection::QueueUpEnd* get_acl_queue_end(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    return connection.queue_->GetUpEnd();
-  }
-
-  void RegisterCallbacks(uint16_t handle, ConnectionManagementCallbacks* callbacks, os::Handler* handler) {
-    auto& connection = check_and_get_connection(handle);
-    connection.command_complete_callbacks_ = callbacks;
-    connection.command_complete_handler_ = handler;
-  }
-
-  void RegisterDisconnectCallback(uint16_t handle, common::OnceCallback<void(ErrorCode)> on_disconnect,
-                                  os::Handler* handler) {
-    auto& connection = check_and_get_connection(handle);
-    connection.on_disconnect_callback_ = std::move(on_disconnect);
-    connection.disconnect_handler_ = handler;
-    if (connection.is_disconnected_) {
-      connection.call_disconnect_callback();
-    }
-  }
-
-  bool Disconnect(uint16_t handle, DisconnectReason reason) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_disconnect, common::Unretained(this), handle, reason));
-    return true;
-  }
-
-  bool ChangeConnectionPacketType(uint16_t handle, uint16_t packet_type) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(
-        BindOnce(&impl::handle_change_connection_packet_type, common::Unretained(this), handle, packet_type));
-    return true;
-  }
-
-  bool AuthenticationRequested(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_authentication_requested, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool SetConnectionEncryption(uint16_t handle, Enable enable) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_set_connection_encryption, common::Unretained(this), handle, enable));
-    return true;
-  }
-
-  bool ChangeConnectionLinkKey(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_change_connection_link_key, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool ReadClockOffset(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_read_clock_offset, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool HoldMode(uint16_t handle, uint16_t max_interval, uint16_t min_interval) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_hold_mode, common::Unretained(this), handle, max_interval, min_interval));
-    return true;
-  }
-
-  bool SniffMode(uint16_t handle, uint16_t max_interval, uint16_t min_interval, int16_t attempt, uint16_t timeout) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_sniff_mode, common::Unretained(this), handle, max_interval, min_interval,
-                            attempt, timeout));
-    return true;
-  }
-
-  bool ExitSniffMode(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_exit_sniff_mode, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool QosSetup(uint16_t handle, ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
-                uint32_t latency, uint32_t delay_variation) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_qos_setup_mode, common::Unretained(this), handle, service_type, token_rate,
-                            peak_bandwidth, latency, delay_variation));
-    return true;
-  }
-
-  bool RoleDiscovery(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_role_discovery, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool ReadLinkPolicySettings(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_read_link_policy_settings, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool WriteLinkPolicySettings(uint16_t handle, uint16_t link_policy_settings) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(
-        BindOnce(&impl::handle_write_link_policy_settings, common::Unretained(this), handle, link_policy_settings));
-    return true;
-  }
-
-  bool FlowSpecification(uint16_t handle, FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
-                         uint32_t token_bucket_size, uint32_t peak_bandwidth, uint32_t access_latency) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_flow_specification, common::Unretained(this), handle, flow_direction,
-                            service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency));
-    return true;
-  }
-
-  bool SniffSubrating(uint16_t handle, uint16_t maximum_latency, uint16_t minimum_remote_timeout,
-                      uint16_t minimum_local_timeout) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_sniff_subrating, common::Unretained(this), handle, maximum_latency,
-                            minimum_remote_timeout, minimum_local_timeout));
-    return true;
-  }
-
-  bool Flush(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_flush, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool ReadAutomaticFlushTimeout(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_read_automatic_flush_timeout, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool WriteAutomaticFlushTimeout(uint16_t handle, uint16_t flush_timeout) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(
-        BindOnce(&impl::handle_write_automatic_flush_timeout, common::Unretained(this), handle, flush_timeout));
-    return true;
-  }
-
-  bool ReadTransmitPowerLevel(uint16_t handle, TransmitPowerLevelType type) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_read_transmit_power_level, common::Unretained(this), handle, type));
-    return true;
-  }
-
-  bool ReadLinkSupervisionTimeout(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_read_link_supervision_timeout, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool WriteLinkSupervisionTimeout(uint16_t handle, uint16_t link_supervision_timeout) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_write_link_supervision_timeout, common::Unretained(this), handle,
-                            link_supervision_timeout));
-    return true;
-  }
-
-  bool ReadFailedContactCounter(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_read_failed_contact_counter, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool ResetFailedContactCounter(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_reset_failed_contact_counter, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool ReadLinkQuality(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_read_link_quality, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool ReadAfhChannelMap(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_afh_channel_map, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool ReadRssi(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_read_rssi, common::Unretained(this), handle));
-    return true;
-  }
-
-  bool ReadClock(uint16_t handle, WhichClock which_clock) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_read_clock, common::Unretained(this), handle, which_clock));
-    return true;
-  }
-
-  bool LeConnectionUpdate(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max,
-                          uint16_t conn_latency, uint16_t supervision_timeout,
-                          common::OnceCallback<void(ErrorCode)> done_callback, os::Handler* handler) {
-    auto& connection = check_and_get_connection(handle);
-    if (connection.is_disconnected_) {
-      LOG_INFO("Already disconnected");
-      return false;
-    }
-    if (!connection.on_connection_update_complete_callback_.is_null()) {
-      LOG_INFO("There is another pending connection update");
-      return false;
-    }
-    connection.on_connection_update_complete_callback_ = std::move(done_callback);
-    connection.on_connection_update_complete_callback_handler_ = handler;
-    if (conn_interval_min < 0x0006 || conn_interval_min > 0x0C80 || conn_interval_max < 0x0006 ||
-        conn_interval_max > 0x0C80 || conn_latency > 0x01F3 || supervision_timeout < 0x000A ||
-        supervision_timeout > 0x0C80) {
-      LOG_ERROR("Invalid parameter");
-      return false;
-    }
-    handler_->Post(BindOnce(&impl::handle_le_connection_update, common::Unretained(this), handle, conn_interval_min,
-                            conn_interval_max, conn_latency, supervision_timeout));
-    return true;
-  }
-
-  void Finish(uint16_t handle) {
-    auto& connection = check_and_get_connection(handle);
-    ASSERT_LOG(connection.is_disconnected_, "Finish must be invoked after disconnection (handle 0x%04hx)", handle);
-    handler_->Post(BindOnce(&impl::cleanup, common::Unretained(this), handle));
-  }
-
   const AclManager& acl_manager_;
 
-  static constexpr uint16_t kMinimumCeLength = 0x0002;
-  static constexpr uint16_t kMaximumCeLength = 0x0C00;
-
-  Controller* controller_ = nullptr;
-  uint16_t max_acl_packet_credits_ = 0;
-  uint16_t acl_packet_credits_ = 0;
-  uint16_t acl_buffer_length_ = 0;
-
-  std::list<std::unique_ptr<AclPacketBuilder>> fragments_to_send_;
-  std::map<uint16_t, acl_connection>::iterator current_connection_pair_;
-
-  HciLayer* hci_layer_ = nullptr;
+  classic_impl* classic_impl_ = nullptr;
+  le_impl* le_impl_ = nullptr;
   os::Handler* handler_ = nullptr;
-  ConnectionCallbacks* client_callbacks_ = nullptr;
-  os::Handler* client_handler_ = nullptr;
-  LeConnectionCallbacks* le_client_callbacks_ = nullptr;
-  os::Handler* le_client_handler_ = nullptr;
-  AclManagerCallbacks* acl_manager_client_callbacks_ = nullptr;
-  os::Handler* acl_manager_client_handler_ = nullptr;
+  Controller* controller_ = nullptr;
+  HciLayer* hci_layer_ = nullptr;
+  RoundRobinScheduler* round_robin_scheduler_ = nullptr;
   common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* hci_queue_end_ = nullptr;
-  std::map<uint16_t, AclManager::acl_connection> acl_connections_;
-  std::set<Address> connecting_;
-  std::set<AddressWithType> connecting_le_;
-  common::Callback<bool(Address, ClassOfDevice)> should_accept_connection_;
-  std::queue<std::pair<Address, std::unique_ptr<CreateConnectionBuilder>>> pending_outgoing_connections_;
-  size_t hci_mtu_{0};
+  std::atomic_bool enqueue_registered_ = false;
+  uint16_t default_link_policy_settings_ = 0xffff;
 };
 
-AclConnection::QueueUpEnd* AclConnection::GetAclQueueEnd() const {
-  return manager_->pimpl_->get_acl_queue_end(handle_);
-}
-
-void AclConnection::RegisterCallbacks(ConnectionManagementCallbacks* callbacks, os::Handler* handler) {
-  return manager_->pimpl_->RegisterCallbacks(handle_, callbacks, handler);
-}
-
-void AclConnection::RegisterDisconnectCallback(common::OnceCallback<void(ErrorCode)> on_disconnect,
-                                               os::Handler* handler) {
-  return manager_->pimpl_->RegisterDisconnectCallback(handle_, std::move(on_disconnect), handler);
-}
-
-bool AclConnection::Disconnect(DisconnectReason reason) {
-  return manager_->pimpl_->Disconnect(handle_, reason);
-}
-
-bool AclConnection::ChangeConnectionPacketType(uint16_t packet_type) {
-  return manager_->pimpl_->ChangeConnectionPacketType(handle_, packet_type);
-}
-
-bool AclConnection::AuthenticationRequested() {
-  return manager_->pimpl_->AuthenticationRequested(handle_);
-}
-
-bool AclConnection::SetConnectionEncryption(Enable enable) {
-  return manager_->pimpl_->SetConnectionEncryption(handle_, enable);
-}
-
-bool AclConnection::ChangeConnectionLinkKey() {
-  return manager_->pimpl_->ChangeConnectionLinkKey(handle_);
-}
-
-bool AclConnection::ReadClockOffset() {
-  return manager_->pimpl_->ReadClockOffset(handle_);
-}
-
-bool AclConnection::HoldMode(uint16_t max_interval, uint16_t min_interval) {
-  return manager_->pimpl_->HoldMode(handle_, max_interval, min_interval);
-}
-
-bool AclConnection::SniffMode(uint16_t max_interval, uint16_t min_interval, uint16_t attempt, uint16_t timeout) {
-  return manager_->pimpl_->SniffMode(handle_, max_interval, min_interval, attempt, timeout);
-}
-
-bool AclConnection::ExitSniffMode() {
-  return manager_->pimpl_->ExitSniffMode(handle_);
-}
-
-bool AclConnection::QosSetup(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
-                             uint32_t delay_variation) {
-  return manager_->pimpl_->QosSetup(handle_, service_type, token_rate, peak_bandwidth, latency, delay_variation);
-}
-
-bool AclConnection::RoleDiscovery() {
-  return manager_->pimpl_->RoleDiscovery(handle_);
-}
-
-bool AclConnection::ReadLinkPolicySettings() {
-  return manager_->pimpl_->ReadLinkPolicySettings(handle_);
-}
-
-bool AclConnection::WriteLinkPolicySettings(uint16_t link_policy_settings) {
-  return manager_->pimpl_->WriteLinkPolicySettings(handle_, link_policy_settings);
-}
-
-bool AclConnection::FlowSpecification(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
-                                      uint32_t token_bucket_size, uint32_t peak_bandwidth, uint32_t access_latency) {
-  return manager_->pimpl_->FlowSpecification(handle_, flow_direction, service_type, token_rate, token_bucket_size,
-                                             peak_bandwidth, access_latency);
-}
-
-bool AclConnection::SniffSubrating(uint16_t maximum_latency, uint16_t minimum_remote_timeout,
-                                   uint16_t minimum_local_timeout) {
-  return manager_->pimpl_->SniffSubrating(handle_, maximum_latency, minimum_remote_timeout, minimum_local_timeout);
-}
-
-bool AclConnection::Flush() {
-  return manager_->pimpl_->Flush(handle_);
-}
-
-bool AclConnection::ReadAutomaticFlushTimeout() {
-  return manager_->pimpl_->ReadAutomaticFlushTimeout(handle_);
-}
-
-bool AclConnection::WriteAutomaticFlushTimeout(uint16_t flush_timeout) {
-  return manager_->pimpl_->WriteAutomaticFlushTimeout(handle_, flush_timeout);
-}
-
-bool AclConnection::ReadTransmitPowerLevel(TransmitPowerLevelType type) {
-  return manager_->pimpl_->ReadTransmitPowerLevel(handle_, type);
-}
-
-bool AclConnection::ReadLinkSupervisionTimeout() {
-  return manager_->pimpl_->ReadLinkSupervisionTimeout(handle_);
-}
-
-bool AclConnection::WriteLinkSupervisionTimeout(uint16_t link_supervision_timeout) {
-  return manager_->pimpl_->WriteLinkSupervisionTimeout(handle_, link_supervision_timeout);
-}
-
-bool AclConnection::ReadFailedContactCounter() {
-  return manager_->pimpl_->ReadFailedContactCounter(handle_);
-}
-
-bool AclConnection::ResetFailedContactCounter() {
-  return manager_->pimpl_->ResetFailedContactCounter(handle_);
-}
-
-bool AclConnection::ReadLinkQuality() {
-  return manager_->pimpl_->ReadLinkQuality(handle_);
-}
-
-bool AclConnection::ReadAfhChannelMap() {
-  return manager_->pimpl_->ReadAfhChannelMap(handle_);
-}
-
-bool AclConnection::ReadRssi() {
-  return manager_->pimpl_->ReadRssi(handle_);
-}
-
-bool AclConnection::ReadClock(WhichClock which_clock) {
-  return manager_->pimpl_->ReadClock(handle_, which_clock);
-}
-
-bool AclConnection::LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
-                                       uint16_t supervision_timeout,
-                                       common::OnceCallback<void(ErrorCode)> done_callback, os::Handler* handler) {
-  return manager_->pimpl_->LeConnectionUpdate(handle_, conn_interval_min, conn_interval_max, conn_latency,
-                                              supervision_timeout, std::move(done_callback), handler);
-}
-
-void AclConnection::Finish() {
-  return manager_->pimpl_->Finish(handle_);
-}
-
 AclManager::AclManager() : pimpl_(std::make_unique<impl>(*this)) {}
 
 void AclManager::RegisterCallbacks(ConnectionCallbacks* callbacks, os::Handler* handler) {
   ASSERT(callbacks != nullptr && handler != nullptr);
-  GetHandler()->Post(common::BindOnce(&impl::handle_register_callbacks, common::Unretained(pimpl_.get()),
-                                      common::Unretained(callbacks), common::Unretained(handler)));
+  GetHandler()->Post(common::BindOnce(&classic_impl::handle_register_callbacks,
+                                      common::Unretained(pimpl_->classic_impl_), common::Unretained(callbacks),
+                                      common::Unretained(handler)));
 }
 
 void AclManager::RegisterLeCallbacks(LeConnectionCallbacks* callbacks, os::Handler* handler) {
   ASSERT(callbacks != nullptr && handler != nullptr);
-  GetHandler()->Post(common::BindOnce(&impl::handle_register_le_callbacks, common::Unretained(pimpl_.get()),
-                                      common::Unretained(callbacks), common::Unretained(handler)));
-}
-
-void AclManager::RegisterAclManagerCallbacks(AclManagerCallbacks* callbacks, os::Handler* handler) {
-  ASSERT(callbacks != nullptr && handler != nullptr);
-  GetHandler()->Post(common::BindOnce(&impl::handle_register_acl_manager_callbacks, common::Unretained(pimpl_.get()),
+  GetHandler()->Post(common::BindOnce(&le_impl::handle_register_le_callbacks, common::Unretained(pimpl_->le_impl_),
                                       common::Unretained(callbacks), common::Unretained(handler)));
 }
 
 void AclManager::CreateConnection(Address address) {
-  GetHandler()->Post(common::BindOnce(&impl::create_connection, common::Unretained(pimpl_.get()), address));
+  GetHandler()->Post(
+      common::BindOnce(&classic_impl::create_connection, common::Unretained(pimpl_->classic_impl_), address));
 }
 
 void AclManager::CreateLeConnection(AddressWithType address_with_type) {
   GetHandler()->Post(
-      common::BindOnce(&impl::create_le_connection, common::Unretained(pimpl_.get()), address_with_type));
+      common::BindOnce(&le_impl::create_le_connection, common::Unretained(pimpl_->le_impl_), address_with_type, true));
+}
+
+void AclManager::SetPrivacyPolicyForInitiatorAddress(
+    LeAddressManager::AddressPolicy address_policy,
+    AddressWithType fixed_address,
+    crypto_toolbox::Octet16 rotation_irk,
+    std::chrono::milliseconds minimum_rotation_time,
+    std::chrono::milliseconds maximum_rotation_time) {
+  GetHandler()->Post(common::BindOnce(&le_impl::set_privacy_policy_for_initiator_address,
+                                      common::Unretained(pimpl_->le_impl_), address_policy, fixed_address, rotation_irk,
+                                      minimum_rotation_time, maximum_rotation_time));
 }
 
 void AclManager::CancelConnect(Address address) {
-  GetHandler()->Post(BindOnce(&impl::cancel_connect, common::Unretained(pimpl_.get()), address));
+  GetHandler()->Post(BindOnce(&classic_impl::cancel_connect, common::Unretained(pimpl_->classic_impl_), address));
+}
+
+void AclManager::CancelLeConnect(AddressWithType address_with_type) {
+  GetHandler()->Post(BindOnce(&le_impl::cancel_connect, common::Unretained(pimpl_->le_impl_), address_with_type));
+}
+
+void AclManager::AddDeviceToConnectList(AddressWithType address_with_type) {
+  GetHandler()->Post(
+      BindOnce(&le_impl::add_device_to_connect_list, common::Unretained(pimpl_->le_impl_), address_with_type));
+}
+
+void AclManager::RemoveDeviceFromConnectList(AddressWithType address_with_type) {
+  GetHandler()->Post(
+      BindOnce(&le_impl::remove_device_from_connect_list, common::Unretained(pimpl_->le_impl_), address_with_type));
 }
 
 void AclManager::MasterLinkKey(KeyFlag key_flag) {
-  GetHandler()->Post(BindOnce(&impl::master_link_key, common::Unretained(pimpl_.get()), key_flag));
+  GetHandler()->Post(BindOnce(&classic_impl::master_link_key, common::Unretained(pimpl_->classic_impl_), key_flag));
 }
 
 void AclManager::SwitchRole(Address address, Role role) {
-  GetHandler()->Post(BindOnce(&impl::switch_role, common::Unretained(pimpl_.get()), address, role));
+  GetHandler()->Post(BindOnce(&classic_impl::switch_role, common::Unretained(pimpl_->classic_impl_), address, role));
 }
 
-void AclManager::ReadDefaultLinkPolicySettings() {
-  GetHandler()->Post(BindOnce(&impl::read_default_link_policy_settings, common::Unretained(pimpl_.get())));
+uint16_t AclManager::ReadDefaultLinkPolicySettings() {
+  ASSERT_LOG(pimpl_->default_link_policy_settings_ != 0xffff, "Settings were never written");
+  return pimpl_->default_link_policy_settings_;
 }
 
 void AclManager::WriteDefaultLinkPolicySettings(uint16_t default_link_policy_settings) {
-  GetHandler()->Post(BindOnce(&impl::write_default_link_policy_settings, common::Unretained(pimpl_.get()),
-                              default_link_policy_settings));
+  pimpl_->default_link_policy_settings_ = default_link_policy_settings;
+  GetHandler()->Post(BindOnce(&classic_impl::write_default_link_policy_settings,
+                              common::Unretained(pimpl_->classic_impl_), default_link_policy_settings));
+}
+
+void AclManager::SetSecurityModule(security::SecurityModule* security_module) {
+  GetHandler()->Post(
+      BindOnce(&classic_impl::set_security_module, common::Unretained(pimpl_->classic_impl_), security_module));
+}
+
+LeAddressManager* AclManager::GetLeAddressManager() {
+  return pimpl_->le_impl_->le_address_manager_;
+}
+
+uint16_t AclManager::HACK_GetHandle(Address address) {
+  return pimpl_->classic_impl_->HACK_get_handle(address);
+}
+
+uint16_t AclManager::HACK_GetLeHandle(Address address) {
+  return pimpl_->le_impl_->HACK_get_handle(address);
 }
 
 void AclManager::ListDependencies(ModuleList* list) {
diff --git a/gd/hci/acl_manager.h b/gd/hci/acl_manager.h
index 1402628..bb4c348 100644
--- a/gd/hci/acl_manager.h
+++ b/gd/hci/acl_manager.h
@@ -20,185 +20,29 @@
 
 #include "common/bidi_queue.h"
 #include "common/callback.h"
+#include "hci/acl_manager/connection_callbacks.h"
+#include "hci/acl_manager/le_connection_callbacks.h"
 #include "hci/address.h"
 #include "hci/address_with_type.h"
 #include "hci/hci_layer.h"
 #include "hci/hci_packets.h"
+#include "hci/le_address_manager.h"
 #include "module.h"
 #include "os/handler.h"
 
 namespace bluetooth {
+
+namespace security {
+class SecurityModule;
+}
+namespace shim {
+class Btm;
+}
+
 namespace hci {
 
-class AclManager;
-
-class ConnectionManagementCallbacks {
- public:
-  virtual ~ConnectionManagementCallbacks() = default;
-  // Invoked when controller sends Connection Packet Type Changed event with Success error code
-  virtual void OnConnectionPacketTypeChanged(uint16_t packet_type) = 0;
-  // Invoked when controller sends Authentication Complete event with Success error code
-  virtual void OnAuthenticationComplete() = 0;
-  // Invoked when controller sends Encryption Change event with Success error code
-  virtual void OnEncryptionChange(EncryptionEnabled enabled) = 0;
-  // Invoked when controller sends Change Connection Link Key Complete event with Success error code
-  virtual void OnChangeConnectionLinkKeyComplete() = 0;
-  // Invoked when controller sends Read Clock Offset Complete event with Success error code
-  virtual void OnReadClockOffsetComplete(uint16_t clock_offset) = 0;
-  // Invoked when controller sends Mode Change event with Success error code
-  virtual void OnModeChange(Mode current_mode, uint16_t interval) = 0;
-  // Invoked when controller sends QoS Setup Complete event with Success error code
-  virtual void OnQosSetupComplete(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
-                                  uint32_t latency, uint32_t delay_variation) = 0;
-  // Invoked when controller sends Flow Specification Complete event with Success error code
-  virtual void OnFlowSpecificationComplete(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
-                                           uint32_t token_bucket_size, uint32_t peak_bandwidth,
-                                           uint32_t access_latency) = 0;
-  // Invoked when controller sends Flush Occurred event
-  virtual void OnFlushOccurred() = 0;
-  // Invoked when controller sends Command Complete event for Role Discovery command with Success error code
-  virtual void OnRoleDiscoveryComplete(Role current_role) = 0;
-  // Invoked when controller sends Command Complete event for Read Link Policy Settings command with Success error code
-  virtual void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) = 0;
-  // Invoked when controller sends Command Complete event for Read Automatic Flush Timeout command with Success error
-  // code
-  virtual void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) = 0;
-  // Invoked when controller sends Command Complete event for Read Transmit Power Level command with Success error code
-  virtual void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) = 0;
-  // Invoked when controller sends Command Complete event for Read Link Supervision Time out command with Success error
-  // code
-  virtual void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) = 0;
-  // Invoked when controller sends Command Complete event for Read Failed Contact Counter command with Success error
-  // code
-  virtual void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) = 0;
-  // Invoked when controller sends Command Complete event for Read Link Quality command with Success error code
-  virtual void OnReadLinkQualityComplete(uint8_t link_quality) = 0;
-  // Invoked when controller sends Command Complete event for Read AFH Channel Map command with Success error code
-  virtual void OnReadAfhChannelMapComplete(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) = 0;
-  // Invoked when controller sends Command Complete event for Read RSSI command with Success error code
-  virtual void OnReadRssiComplete(uint8_t rssi) = 0;
-  // Invoked when controller sends Command Complete event for Read Clock command with Success error code
-  virtual void OnReadClockComplete(uint32_t clock, uint16_t accuracy) = 0;
-};
-
-class AclConnection {
- public:
-  AclConnection()
-      : manager_(nullptr), handle_(0), address_(Address::kEmpty), address_type_(AddressType::PUBLIC_DEVICE_ADDRESS){};
-  virtual ~AclConnection() = default;
-
-  virtual Address GetAddress() const {
-    return address_;
-  }
-
-  virtual AddressType GetAddressType() const {
-    return address_type_;
-  }
-
-  uint16_t GetHandle() const {
-    return handle_;
-  }
-
-  /* This return role for LE devices only, for Classic, please see |RoleDiscovery| method.
-   * TODO: split AclConnection for LE and Classic
-   */
-  Role GetRole() const {
-    return role_;
-  }
-
-  using Queue = common::BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder>;
-  using QueueUpEnd = common::BidiQueueEnd<BasePacketBuilder, PacketView<kLittleEndian>>;
-  using QueueDownEnd = common::BidiQueueEnd<PacketView<kLittleEndian>, BasePacketBuilder>;
-  virtual QueueUpEnd* GetAclQueueEnd() const;
-  virtual void RegisterCallbacks(ConnectionManagementCallbacks* callbacks, os::Handler* handler);
-  virtual void RegisterDisconnectCallback(common::OnceCallback<void(ErrorCode)> on_disconnect, os::Handler* handler);
-  virtual bool Disconnect(DisconnectReason reason);
-  virtual bool ChangeConnectionPacketType(uint16_t packet_type);
-  virtual bool AuthenticationRequested();
-  virtual bool SetConnectionEncryption(Enable enable);
-  virtual bool ChangeConnectionLinkKey();
-  virtual bool ReadClockOffset();
-  virtual bool HoldMode(uint16_t max_interval, uint16_t min_interval);
-  virtual bool SniffMode(uint16_t max_interval, uint16_t min_interval, uint16_t attempt, uint16_t timeout);
-  virtual bool ExitSniffMode();
-  virtual bool QosSetup(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
-                        uint32_t delay_variation);
-  virtual bool RoleDiscovery();
-  virtual bool ReadLinkPolicySettings();
-  virtual bool WriteLinkPolicySettings(uint16_t link_policy_settings);
-  virtual bool FlowSpecification(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
-                                 uint32_t token_bucket_size, uint32_t peak_bandwidth, uint32_t access_latency);
-  virtual bool SniffSubrating(uint16_t maximum_latency, uint16_t minimum_remote_timeout,
-                              uint16_t minimum_local_timeout);
-  virtual bool Flush();
-  virtual bool ReadAutomaticFlushTimeout();
-  virtual bool WriteAutomaticFlushTimeout(uint16_t flush_timeout);
-  virtual bool ReadTransmitPowerLevel(TransmitPowerLevelType type);
-  virtual bool ReadLinkSupervisionTimeout();
-  virtual bool WriteLinkSupervisionTimeout(uint16_t link_supervision_timeout);
-  virtual bool ReadFailedContactCounter();
-  virtual bool ResetFailedContactCounter();
-  virtual bool ReadLinkQuality();
-  virtual bool ReadAfhChannelMap();
-  virtual bool ReadRssi();
-  virtual bool ReadClock(WhichClock which_clock);
-
-  // LE ACL Method
-  virtual bool LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
-                                  uint16_t supervision_timeout, common::OnceCallback<void(ErrorCode)> done_callback,
-                                  os::Handler* handler);
-
-  // Ask AclManager to clean me up. Must invoke after on_disconnect is called
-  virtual void Finish();
-
-  // TODO: API to change link settings ... ?
-
- private:
-  friend AclManager;
-  AclConnection(const AclManager* manager, uint16_t handle, Address address)
-      : manager_(manager), handle_(handle), address_(address), address_type_(AddressType::PUBLIC_DEVICE_ADDRESS) {}
-  AclConnection(const AclManager* manager, uint16_t handle, Address address, AddressType address_type, Role role)
-      : manager_(manager), handle_(handle), address_(address), address_type_(address_type), role_(role) {}
-  const AclManager* manager_;
-  uint16_t handle_;
-  Address address_;
-  AddressType address_type_;
-  Role role_;
-  DISALLOW_COPY_AND_ASSIGN(AclConnection);
-};
-
-class ConnectionCallbacks {
- public:
-  virtual ~ConnectionCallbacks() = default;
-  // Invoked when controller sends Connection Complete event with Success error code
-  virtual void OnConnectSuccess(std::unique_ptr<AclConnection> /* , initiated_by_local ? */) = 0;
-  // Invoked when controller sends Connection Complete event with non-Success error code
-  virtual void OnConnectFail(Address, ErrorCode reason) = 0;
-};
-
-class LeConnectionCallbacks {
- public:
-  virtual ~LeConnectionCallbacks() = default;
-  // Invoked when controller sends Connection Complete event with Success error code
-  // AddressWithType is always equal to the object used in AclManager#CreateLeConnection
-  virtual void OnLeConnectSuccess(AddressWithType, std::unique_ptr<AclConnection> /* , initiated_by_local ? */) = 0;
-  // Invoked when controller sends Connection Complete event with non-Success error code
-  virtual void OnLeConnectFail(AddressWithType, ErrorCode reason) = 0;
-};
-
-class AclManagerCallbacks {
- public:
-  virtual ~AclManagerCallbacks() = default;
-  // Invoked when controller sends Master Link Key Complete event with Success error code
-  virtual void OnMasterLinkKeyComplete(uint16_t connection_handle, KeyFlag key_flag) = 0;
-  // Invoked when controller sends Role Change event with Success error code
-  virtual void OnRoleChange(Address bd_addr, Role new_role) = 0;
-  // Invoked when controller sends Command Complete event for Read Default Link Policy Settings command with Success
-  // error code
-  virtual void OnReadDefaultLinkPolicySettingsComplete(uint16_t default_link_policy_settings) = 0;
-};
-
 class AclManager : public Module {
+ friend class bluetooth::shim::Btm;
  public:
   AclManager();
   // NOTE: It is necessary to forward declare a default destructor that overrides the base class one, because
@@ -209,13 +53,10 @@
 
   // Should register only once when user module starts.
   // Generates OnConnectSuccess when an incoming connection is established.
-  virtual void RegisterCallbacks(ConnectionCallbacks* callbacks, os::Handler* handler);
+  virtual void RegisterCallbacks(acl_manager::ConnectionCallbacks* callbacks, os::Handler* handler);
 
   // Should register only once when user module starts.
-  virtual void RegisterLeCallbacks(LeConnectionCallbacks* callbacks, os::Handler* handler);
-
-  // Should register only once when user module starts.
-  virtual void RegisterAclManagerCallbacks(AclManagerCallbacks* callbacks, os::Handler* handler);
+  virtual void RegisterLeCallbacks(acl_manager::LeConnectionCallbacks* callbacks, os::Handler* handler);
 
   // Generates OnConnectSuccess if connected, or OnConnectFail otherwise
   virtual void CreateConnection(Address address);
@@ -223,15 +64,31 @@
   // Generates OnLeConnectSuccess if connected, or OnLeConnectFail otherwise
   virtual void CreateLeConnection(AddressWithType address_with_type);
 
+  virtual void SetPrivacyPolicyForInitiatorAddress(
+      LeAddressManager::AddressPolicy address_policy,
+      AddressWithType fixed_address,
+      crypto_toolbox::Octet16 rotation_irk,
+      std::chrono::milliseconds minimum_rotation_time,
+      std::chrono::milliseconds maximum_rotation_time);
+
   // Generates OnConnectFail with error code "terminated by local host 0x16" if cancelled, or OnConnectSuccess if not
   // successfully cancelled and already connected
   virtual void CancelConnect(Address address);
 
+  virtual void CancelLeConnect(AddressWithType address_with_type);
+  virtual void AddDeviceToConnectList(AddressWithType address_with_type);
+  virtual void RemoveDeviceFromConnectList(AddressWithType address_with_type);
+
   virtual void MasterLinkKey(KeyFlag key_flag);
   virtual void SwitchRole(Address address, Role role);
-  virtual void ReadDefaultLinkPolicySettings();
+  virtual uint16_t ReadDefaultLinkPolicySettings();
   virtual void WriteDefaultLinkPolicySettings(uint16_t default_link_policy_settings);
 
+  // In order to avoid circular dependency use setter rather than module dependency.
+  virtual void SetSecurityModule(security::SecurityModule* security_module);
+
+  virtual LeAddressManager* GetLeAddressManager();
+
   static const ModuleFactory Factory;
 
  protected:
@@ -244,12 +101,12 @@
   std::string ToString() const override;
 
  private:
-  friend AclConnection;
+  virtual uint16_t HACK_GetHandle(const Address address);
+  virtual uint16_t HACK_GetLeHandle(const Address address);
 
   struct impl;
   std::unique_ptr<impl> pimpl_;
 
-  struct acl_connection;
   DISALLOW_COPY_AND_ASSIGN(AclManager);
 };
 
diff --git a/gd/l2cap/security_policy.h b/gd/hci/acl_manager/acl_connection.cc
similarity index 68%
copy from gd/l2cap/security_policy.h
copy to gd/hci/acl_manager/acl_connection.cc
index 5a06401..c678bf9 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/hci/acl_manager/acl_connection.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,12 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
+
+#include "hci/acl_manager/acl_connection.h"
 
 namespace bluetooth {
-namespace l2cap {
+namespace hci {
+namespace acl_manager {
 
-class SecurityPolicy {};
+AclConnection::QueueUpEnd* AclConnection::GetAclQueueEnd() const {
+  return queue_up_end_;
+}
 
-}  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/acl_connection.h b/gd/hci/acl_manager/acl_connection.h
new file mode 100644
index 0000000..7981bf8
--- /dev/null
+++ b/gd/hci/acl_manager/acl_connection.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "common/bidi_queue.h"
+#include "hci/hci_packets.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class AclConnection {
+ public:
+  AclConnection() : queue_up_end_(nullptr), handle_(0){};
+  virtual ~AclConnection() = default;
+
+  uint16_t GetHandle() const {
+    return handle_;
+  }
+
+  using Queue = common::BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder>;
+  using QueueUpEnd = common::BidiQueueEnd<BasePacketBuilder, PacketView<kLittleEndian>>;
+  using QueueDownEnd = common::BidiQueueEnd<PacketView<kLittleEndian>, BasePacketBuilder>;
+  virtual QueueUpEnd* GetAclQueueEnd() const;
+
+ protected:
+  AclConnection(QueueUpEnd* queue_up_end, uint16_t handle) : queue_up_end_(queue_up_end), handle_(handle) {}
+  QueueUpEnd* queue_up_end_;
+  uint16_t handle_;
+  DISALLOW_COPY_AND_ASSIGN(AclConnection);
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_fragmenter.cc b/gd/hci/acl_manager/acl_fragmenter.cc
similarity index 92%
rename from gd/hci/acl_fragmenter.cc
rename to gd/hci/acl_manager/acl_fragmenter.cc
index fa3b31d..16cbe69 100644
--- a/gd/hci/acl_fragmenter.cc
+++ b/gd/hci/acl_manager/acl_fragmenter.cc
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-#include "hci/acl_fragmenter.h"
+#include "hci/acl_manager/acl_fragmenter.h"
 
 #include "os/log.h"
 #include "packet/fragmenting_inserter.h"
 
 namespace bluetooth {
 namespace hci {
+namespace acl_manager {
 
 AclFragmenter::AclFragmenter(size_t mtu, std::unique_ptr<packet::BasePacketBuilder> packet)
     : mtu_(mtu), packet_(std::move(packet)) {}
@@ -33,5 +34,6 @@
   return to_return;
 }
 
+}  // namespace acl_manager
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/acl_fragmenter.h b/gd/hci/acl_manager/acl_fragmenter.h
similarity index 95%
rename from gd/hci/acl_fragmenter.h
rename to gd/hci/acl_manager/acl_fragmenter.h
index 4bc3d06..37badef 100644
--- a/gd/hci/acl_fragmenter.h
+++ b/gd/hci/acl_manager/acl_fragmenter.h
@@ -27,6 +27,7 @@
 
 namespace bluetooth {
 namespace hci {
+namespace acl_manager {
 
 class AclFragmenter {
  public:
@@ -40,5 +41,6 @@
   std::unique_ptr<packet::BasePacketBuilder> packet_;
 };
 
+}  // namespace acl_manager
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/acl_manager/assembler.h b/gd/hci/acl_manager/assembler.h
new file mode 100644
index 0000000..186a5c0
--- /dev/null
+++ b/gd/hci/acl_manager/assembler.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+namespace {
+class PacketViewForRecombination : public packet::PacketView<kLittleEndian> {
+ public:
+  PacketViewForRecombination(const PacketView& packetView) : PacketView(packetView) {}
+  void AppendPacketView(packet::PacketView<kLittleEndian> to_append) {
+    Append(to_append);
+  }
+};
+
+constexpr size_t kMaxQueuedPacketsPerConnection = 10;
+constexpr int kL2capBasicFrameHeaderSize = 4;
+
+// Per spec 5.1 Vol 2 Part B 5.3, ACL link shall carry L2CAP data. Therefore, an ACL packet shall contain L2CAP PDU.
+// This function returns the PDU size of the L2CAP data if it's a starting packet. Returns 0 if it's invalid.
+uint16_t GetL2capPduSize(AclPacketView packet) {
+  auto l2cap_payload = packet.GetPayload();
+  if (l2cap_payload.size() < kL2capBasicFrameHeaderSize) {
+    LOG_ERROR("Controller sent an invalid L2CAP starting packet!");
+    return 0;
+  }
+  return (l2cap_payload.at(1) << 8u) + l2cap_payload.at(0);
+}
+
+}  // namespace
+
+struct assembler {
+  assembler(AddressWithType address_with_type, AclConnection::QueueDownEnd* down_end, os::Handler* handler)
+      : address_with_type_(address_with_type), down_end_(down_end), handler_(handler) {}
+  AddressWithType address_with_type_;
+  AclConnection::QueueDownEnd* down_end_;
+  os::Handler* handler_;
+  PacketViewForRecombination recombination_stage_{std::make_shared<std::vector<uint8_t>>()};
+  int remaining_sdu_continuation_packet_size_ = 0;
+  std::shared_ptr<std::atomic_bool> enqueue_registered_ = std::make_shared<std::atomic_bool>(false);
+  std::queue<packet::PacketView<kLittleEndian>> incoming_queue_;
+
+  ~assembler() {
+    if (enqueue_registered_->exchange(false)) {
+      down_end_->UnregisterEnqueue();
+    }
+  }
+
+  // Invoked from some external Queue Reactable context
+  std::unique_ptr<packet::PacketView<kLittleEndian>> on_le_incoming_data_ready() {
+    auto packet = incoming_queue_.front();
+    incoming_queue_.pop();
+    if (incoming_queue_.empty() && enqueue_registered_->exchange(false)) {
+      down_end_->UnregisterEnqueue();
+    }
+    return std::make_unique<PacketView<kLittleEndian>>(packet);
+  }
+
+  void on_incoming_packet(AclPacketView packet) {
+    PacketView<kLittleEndian> payload = packet.GetPayload();
+    auto payload_size = payload.size();
+    auto packet_boundary_flag = packet.GetPacketBoundaryFlag();
+    if (packet_boundary_flag == PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE) {
+      LOG_ERROR("Controller is not allowed to send FIRST_NON_AUTOMATICALLY_FLUSHABLE to host except loopback mode");
+      return;
+    }
+    if (packet_boundary_flag == PacketBoundaryFlag::CONTINUING_FRAGMENT) {
+      if (remaining_sdu_continuation_packet_size_ < payload_size) {
+        LOG_WARN("Remote sent unexpected L2CAP PDU. Drop the entire L2CAP PDU");
+        recombination_stage_ = PacketViewForRecombination(std::make_shared<std::vector<uint8_t>>());
+        remaining_sdu_continuation_packet_size_ = 0;
+        return;
+      }
+      remaining_sdu_continuation_packet_size_ -= payload_size;
+      recombination_stage_.AppendPacketView(payload);
+      if (remaining_sdu_continuation_packet_size_ != 0) {
+        return;
+      } else {
+        payload = recombination_stage_;
+        recombination_stage_ = PacketViewForRecombination(std::make_shared<std::vector<uint8_t>>());
+      }
+    } else if (packet_boundary_flag == PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE) {
+      if (recombination_stage_.size() > 0) {
+        LOG_ERROR("Controller sent a starting packet without finishing previous packet. Drop previous one.");
+      }
+      auto l2cap_pdu_size = GetL2capPduSize(packet);
+      remaining_sdu_continuation_packet_size_ = l2cap_pdu_size - (payload_size - kL2capBasicFrameHeaderSize);
+      if (remaining_sdu_continuation_packet_size_ > 0) {
+        recombination_stage_ = payload;
+        return;
+      }
+    }
+    if (incoming_queue_.size() > kMaxQueuedPacketsPerConnection) {
+      LOG_ERROR("Dropping packet from %s due to congestion", address_with_type_.ToString().c_str());
+      return;
+    }
+
+    incoming_queue_.push(payload);
+    if (!enqueue_registered_->exchange(true)) {
+      down_end_->RegisterEnqueue(handler_,
+                                 common::Bind(&assembler::on_le_incoming_data_ready, common::Unretained(this)));
+    }
+  }
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/classic_acl_connection.cc b/gd/hci/acl_manager/classic_acl_connection.cc
new file mode 100644
index 0000000..c36d6b2
--- /dev/null
+++ b/gd/hci/acl_manager/classic_acl_connection.cc
@@ -0,0 +1,549 @@
+/*
+ * 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.
+ */
+
+#include "hci/acl_manager/classic_acl_connection.h"
+
+#include "hci/acl_manager/event_checkers.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class AclConnectionTracker : public ConnectionManagementCallbacks {
+ public:
+  AclConnectionTracker(AclConnectionInterface* acl_connection_interface)
+      : acl_connection_interface_(acl_connection_interface) {}
+  ~AclConnectionTracker() override {
+    // If callbacks were registered, they should have been delivered.
+    ASSERT(client_callbacks_ == nullptr || queued_callbacks_.empty());
+  }
+  void RegisterCallbacks(ConnectionManagementCallbacks* callbacks, os::Handler* handler) {
+    while (!queued_callbacks_.empty()) {
+      auto iter = queued_callbacks_.begin();
+      handler->Post(std::move(*iter));
+      queued_callbacks_.erase(iter);
+    }
+    client_handler_ = handler;
+    client_callbacks_ = callbacks;
+  }
+
+#define SAVE_OR_CALL(f, ...)                                                                                        \
+  if (client_handler_ == nullptr) {                                                                                 \
+    queued_callbacks_.emplace_back(                                                                                 \
+        common::BindOnce(&ConnectionManagementCallbacks::f, common::Unretained(this), ##__VA_ARGS__));              \
+  } else {                                                                                                          \
+    client_handler_->Post(                                                                                          \
+        common::BindOnce(&ConnectionManagementCallbacks::f, common::Unretained(client_callbacks_), ##__VA_ARGS__)); \
+  }
+
+  void OnConnectionPacketTypeChanged(uint16_t packet_type) override {
+    SAVE_OR_CALL(OnConnectionPacketTypeChanged, packet_type)
+  }
+  void OnAuthenticationComplete() override {
+    SAVE_OR_CALL(OnAuthenticationComplete)
+  }
+  void OnEncryptionChange(EncryptionEnabled enabled) override {
+    SAVE_OR_CALL(OnEncryptionChange, enabled)
+  }
+  void OnChangeConnectionLinkKeyComplete() override {
+    SAVE_OR_CALL(OnChangeConnectionLinkKeyComplete)
+  }
+  void OnReadClockOffsetComplete(uint16_t clock_offset) override {
+    SAVE_OR_CALL(OnReadClockOffsetComplete, clock_offset)
+  }
+  void OnModeChange(Mode current_mode, uint16_t interval) override {
+    SAVE_OR_CALL(OnModeChange, current_mode, interval)
+  }
+  void OnQosSetupComplete(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
+                          uint32_t delay_variation) override {
+    SAVE_OR_CALL(OnQosSetupComplete, service_type, token_rate, peak_bandwidth, latency, delay_variation)
+  }
+  void OnFlowSpecificationComplete(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                                   uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                   uint32_t access_latency) override {
+    SAVE_OR_CALL(OnFlowSpecificationComplete, flow_direction, service_type, token_rate, token_bucket_size,
+                 peak_bandwidth, access_latency)
+  }
+  void OnFlushOccurred() override {
+    SAVE_OR_CALL(OnFlushOccurred)
+  }
+  void OnRoleDiscoveryComplete(Role current_role) override {
+    SAVE_OR_CALL(OnRoleDiscoveryComplete, current_role)
+  }
+  void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) override {
+    SAVE_OR_CALL(OnReadLinkPolicySettingsComplete, link_policy_settings)
+  }
+  void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) override {
+    SAVE_OR_CALL(OnReadAutomaticFlushTimeoutComplete, flush_timeout)
+  }
+  void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) override {
+    SAVE_OR_CALL(OnReadTransmitPowerLevelComplete, transmit_power_level)
+  }
+  void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) override {
+    SAVE_OR_CALL(OnReadLinkSupervisionTimeoutComplete, link_supervision_timeout)
+  }
+  void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) override {
+    SAVE_OR_CALL(OnReadFailedContactCounterComplete, failed_contact_counter)
+  }
+  void OnReadLinkQualityComplete(uint8_t link_quality) override {
+    SAVE_OR_CALL(OnReadLinkQualityComplete, link_quality)
+  }
+  void OnReadAfhChannelMapComplete(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) override {
+    SAVE_OR_CALL(OnReadAfhChannelMapComplete, afh_mode, afh_channel_map)
+  }
+  void OnReadRssiComplete(uint8_t rssi) override {
+    SAVE_OR_CALL(OnReadRssiComplete, rssi)
+  }
+  void OnReadClockComplete(uint32_t clock, uint16_t accuracy) override {
+    SAVE_OR_CALL(OnReadClockComplete, clock, accuracy)
+  }
+  void OnMasterLinkKeyComplete(KeyFlag key_flag) override {
+    SAVE_OR_CALL(OnMasterLinkKeyComplete, key_flag)
+  }
+  void OnRoleChange(Role new_role) override {
+    SAVE_OR_CALL(OnRoleChange, new_role)
+  }
+  void OnDisconnection(ErrorCode reason) {
+    SAVE_OR_CALL(OnDisconnection, reason);
+  }
+#undef SAVE_OR_CALL
+
+  void on_role_discovery_complete(CommandCompleteView view) {
+    auto complete_view = RoleDiscoveryCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_role_discovery_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_role_discovery_complete with error code %s", error_code.c_str());
+      return;
+    }
+    OnRoleDiscoveryComplete(complete_view.GetCurrentRole());
+  }
+
+  void on_read_link_policy_settings_complete(CommandCompleteView view) {
+    auto complete_view = ReadLinkPolicySettingsCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_link_policy_settings_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_link_policy_settings_complete with error code %s", error_code.c_str());
+      return;
+    }
+    OnReadLinkPolicySettingsComplete(complete_view.GetLinkPolicySettings());
+  }
+
+  void on_read_automatic_flush_timeout_complete(CommandCompleteView view) {
+    auto complete_view = ReadAutomaticFlushTimeoutCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_automatic_flush_timeout_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_automatic_flush_timeout_complete with error code %s", error_code.c_str());
+      return;
+    }
+    OnReadAutomaticFlushTimeoutComplete(complete_view.GetFlushTimeout());
+  }
+
+  void on_read_transmit_power_level_complete(CommandCompleteView view) {
+    auto complete_view = ReadTransmitPowerLevelCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_transmit_power_level_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_transmit_power_level_complete with error code %s", error_code.c_str());
+      return;
+    }
+    OnReadTransmitPowerLevelComplete(complete_view.GetTransmitPowerLevel());
+  }
+
+  void on_read_link_supervision_timeout_complete(CommandCompleteView view) {
+    auto complete_view = ReadLinkSupervisionTimeoutCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_link_supervision_timeout_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_link_supervision_timeout_complete with error code %s", error_code.c_str());
+      return;
+    }
+    OnReadLinkSupervisionTimeoutComplete(complete_view.GetLinkSupervisionTimeout());
+  }
+
+  void on_read_failed_contact_counter_complete(CommandCompleteView view) {
+    auto complete_view = ReadFailedContactCounterCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_failed_contact_counter_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_failed_contact_counter_complete with error code %s", error_code.c_str());
+      return;
+    }
+    OnReadFailedContactCounterComplete(complete_view.GetFailedContactCounter());
+  }
+
+  void on_read_link_quality_complete(CommandCompleteView view) {
+    auto complete_view = ReadLinkQualityCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_link_quality_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_link_quality_complete with error code %s", error_code.c_str());
+      return;
+    }
+    OnReadLinkQualityComplete(complete_view.GetLinkQuality());
+  }
+
+  void on_read_afh_channel_map_complete(CommandCompleteView view) {
+    auto complete_view = ReadAfhChannelMapCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_afh_channel_map_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_afh_channel_map_complete with error code %s", error_code.c_str());
+      return;
+    }
+    OnReadAfhChannelMapComplete(complete_view.GetAfhMode(), complete_view.GetAfhChannelMap());
+  }
+
+  void on_read_rssi_complete(CommandCompleteView view) {
+    auto complete_view = ReadRssiCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_rssi_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_rssi_complete with error code %s", error_code.c_str());
+      return;
+    }
+    OnReadRssiComplete(complete_view.GetRssi());
+  }
+
+  void on_read_remote_version_information_status(CommandStatusView view) {
+    ASSERT_LOG(view.IsValid(), "Bad status packet!");
+    LOG_INFO("UNIMPLEMENTED called: %s", hci::ErrorCodeText(view.GetStatus()).c_str());
+  }
+
+  void on_read_remote_supported_features_status(CommandStatusView view) {
+    ASSERT_LOG(view.IsValid(), "Bad status packet!");
+    LOG_INFO("UNIMPLEMENTED called: %s", hci::ErrorCodeText(view.GetStatus()).c_str());
+  }
+
+  void on_read_remote_extended_features_status(CommandStatusView view) {
+    ASSERT_LOG(view.IsValid(), "Broken");
+    LOG_INFO("UNIMPLEMENTED called: %s", hci::ErrorCodeText(view.GetStatus()).c_str());
+  }
+  void on_read_clock_complete(CommandCompleteView view) {
+    auto complete_view = ReadClockCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_clock_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_clock_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint32_t clock = complete_view.GetClock();
+    uint16_t accuracy = complete_view.GetAccuracy();
+    OnReadClockComplete(clock, accuracy);
+  }
+
+  AclConnectionInterface* acl_connection_interface_;
+  os::Handler* client_handler_ = nullptr;
+  ConnectionManagementCallbacks* client_callbacks_ = nullptr;
+  std::list<common::OnceClosure> queued_callbacks_;
+};
+
+struct ClassicAclConnection::impl {
+  impl(AclConnectionInterface* acl_connection_interface, std::shared_ptr<Queue> queue)
+      : tracker(acl_connection_interface), queue_(std::move(queue)) {}
+  ConnectionManagementCallbacks* GetEventCallbacks() {
+    ASSERT(!callbacks_given_);
+    callbacks_given_ = true;
+    return &tracker;
+  }
+
+  bool callbacks_given_{false};
+  AclConnectionTracker tracker;
+  std::shared_ptr<Queue> queue_;
+};
+
+ClassicAclConnection::ClassicAclConnection()
+    : AclConnection(), acl_connection_interface_(nullptr), address_(Address::kEmpty) {}
+
+ClassicAclConnection::ClassicAclConnection(std::shared_ptr<Queue> queue,
+                                           AclConnectionInterface* acl_connection_interface, uint16_t handle,
+                                           Address address)
+    : AclConnection(queue->GetUpEnd(), handle), acl_connection_interface_(acl_connection_interface), address_(address) {
+  pimpl_ = new ClassicAclConnection::impl(acl_connection_interface, std::move(queue));
+}
+
+ClassicAclConnection::~ClassicAclConnection() {
+  delete pimpl_;
+}
+
+ConnectionManagementCallbacks* ClassicAclConnection::GetEventCallbacks() {
+  return pimpl_->GetEventCallbacks();
+}
+
+void ClassicAclConnection::RegisterCallbacks(ConnectionManagementCallbacks* callbacks, os::Handler* handler) {
+  return pimpl_->tracker.RegisterCallbacks(callbacks, handler);
+}
+
+bool ClassicAclConnection::Disconnect(DisconnectReason reason) {
+  acl_connection_interface_->EnqueueCommand(
+      DisconnectBuilder::Create(handle_, reason),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<DisconnectStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::ChangeConnectionPacketType(uint16_t packet_type) {
+  acl_connection_interface_->EnqueueCommand(
+      ChangeConnectionPacketTypeBuilder::Create(handle_, packet_type),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<ChangeConnectionPacketTypeStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::AuthenticationRequested() {
+  acl_connection_interface_->EnqueueCommand(
+      AuthenticationRequestedBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<AuthenticationRequestedStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::SetConnectionEncryption(Enable enable) {
+  acl_connection_interface_->EnqueueCommand(
+      SetConnectionEncryptionBuilder::Create(handle_, enable),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<SetConnectionEncryptionStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::ChangeConnectionLinkKey() {
+  acl_connection_interface_->EnqueueCommand(
+      ChangeConnectionLinkKeyBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<ChangeConnectionLinkKeyStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::ReadClockOffset() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadClockOffsetBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<ReadClockOffsetStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::HoldMode(uint16_t max_interval, uint16_t min_interval) {
+  acl_connection_interface_->EnqueueCommand(
+      HoldModeBuilder::Create(handle_, max_interval, min_interval),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<HoldModeStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::SniffMode(uint16_t max_interval, uint16_t min_interval, uint16_t attempt, uint16_t timeout) {
+  acl_connection_interface_->EnqueueCommand(
+      SniffModeBuilder::Create(handle_, max_interval, min_interval, attempt, timeout),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<SniffModeStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::ExitSniffMode() {
+  acl_connection_interface_->EnqueueCommand(
+      ExitSniffModeBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<ExitSniffModeStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::QosSetup(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
+                                    uint32_t latency, uint32_t delay_variation) {
+  acl_connection_interface_->EnqueueCommand(
+      QosSetupBuilder::Create(handle_, service_type, token_rate, peak_bandwidth, latency, delay_variation),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<QosSetupStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::RoleDiscovery() {
+  acl_connection_interface_->EnqueueCommand(
+      RoleDiscoveryBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker, &AclConnectionTracker::on_role_discovery_complete));
+  return true;
+}
+
+bool ClassicAclConnection::ReadLinkPolicySettings() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadLinkPolicySettingsBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_link_policy_settings_complete));
+  return true;
+}
+
+bool ClassicAclConnection::WriteLinkPolicySettings(uint16_t link_policy_settings) {
+  acl_connection_interface_->EnqueueCommand(
+      WriteLinkPolicySettingsBuilder::Create(handle_, link_policy_settings),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_complete<WriteLinkPolicySettingsCompleteView>));
+  return true;
+}
+
+bool ClassicAclConnection::FlowSpecification(FlowDirection flow_direction, ServiceType service_type,
+                                             uint32_t token_rate, uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                             uint32_t access_latency) {
+  acl_connection_interface_->EnqueueCommand(
+      FlowSpecificationBuilder::Create(handle_, flow_direction, service_type, token_rate, token_bucket_size,
+                                       peak_bandwidth, access_latency),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_status<FlowSpecificationStatusView>));
+  return true;
+}
+
+bool ClassicAclConnection::SniffSubrating(uint16_t maximum_latency, uint16_t minimum_remote_timeout,
+                                          uint16_t minimum_local_timeout) {
+  acl_connection_interface_->EnqueueCommand(
+      SniffSubratingBuilder::Create(handle_, maximum_latency, minimum_remote_timeout, minimum_local_timeout),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_complete<SniffSubratingCompleteView>));
+  return true;
+}
+
+bool ClassicAclConnection::Flush() {
+  acl_connection_interface_->EnqueueCommand(
+      FlushBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_complete<FlushCompleteView>));
+  return true;
+}
+
+bool ClassicAclConnection::ReadAutomaticFlushTimeout() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadAutomaticFlushTimeoutBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_automatic_flush_timeout_complete));
+  return true;
+}
+
+bool ClassicAclConnection::WriteAutomaticFlushTimeout(uint16_t flush_timeout) {
+  acl_connection_interface_->EnqueueCommand(
+      WriteAutomaticFlushTimeoutBuilder::Create(handle_, flush_timeout),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_complete<WriteAutomaticFlushTimeoutCompleteView>));
+  return true;
+}
+
+bool ClassicAclConnection::ReadTransmitPowerLevel(TransmitPowerLevelType type) {
+  acl_connection_interface_->EnqueueCommand(
+      ReadTransmitPowerLevelBuilder::Create(handle_, type),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_transmit_power_level_complete));
+  return true;
+}
+
+bool ClassicAclConnection::ReadLinkSupervisionTimeout() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadLinkSupervisionTimeoutBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_link_supervision_timeout_complete));
+  return true;
+}
+
+bool ClassicAclConnection::WriteLinkSupervisionTimeout(uint16_t link_supervision_timeout) {
+  acl_connection_interface_->EnqueueCommand(
+      WriteLinkSupervisionTimeoutBuilder::Create(handle_, link_supervision_timeout),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_complete<WriteLinkSupervisionTimeoutCompleteView>));
+  return true;
+}
+
+bool ClassicAclConnection::ReadFailedContactCounter() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadFailedContactCounterBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_failed_contact_counter_complete));
+  return true;
+}
+
+bool ClassicAclConnection::ResetFailedContactCounter() {
+  acl_connection_interface_->EnqueueCommand(
+      ResetFailedContactCounterBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnce(&check_command_complete<ResetFailedContactCounterCompleteView>));
+  return true;
+}
+
+bool ClassicAclConnection::ReadLinkQuality() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadLinkQualityBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_link_quality_complete));
+  return true;
+}
+
+bool ClassicAclConnection::ReadAfhChannelMap() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadAfhChannelMapBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_afh_channel_map_complete));
+  return true;
+}
+
+bool ClassicAclConnection::ReadRssi() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadRssiBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker, &AclConnectionTracker::on_read_rssi_complete));
+  return true;
+}
+
+bool ClassicAclConnection::ReadRemoteVersionInformation() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadRemoteVersionInformationBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_remote_version_information_status));
+  return true;
+}
+
+bool ClassicAclConnection::ReadRemoteSupportedFeatures() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadRemoteSupportedFeaturesBuilder::Create(handle_),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_remote_supported_features_status));
+  return true;
+}
+
+bool ClassicAclConnection::ReadRemoteExtendedFeatures() {
+  acl_connection_interface_->EnqueueCommand(
+      ReadRemoteExtendedFeaturesBuilder::Create(handle_, 1),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker,
+                                                  &AclConnectionTracker::on_read_remote_extended_features_status));
+  return true;
+}
+
+bool ClassicAclConnection::ReadClock(WhichClock which_clock) {
+  pimpl_->tracker.acl_connection_interface_->EnqueueCommand(
+      ReadClockBuilder::Create(handle_, which_clock),
+      pimpl_->tracker.client_handler_->BindOnceOn(&pimpl_->tracker, &AclConnectionTracker::on_read_clock_complete));
+  return true;
+}
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/classic_acl_connection.h b/gd/hci/acl_manager/classic_acl_connection.h
new file mode 100644
index 0000000..179b729
--- /dev/null
+++ b/gd/hci/acl_manager/classic_acl_connection.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "hci/acl_connection_interface.h"
+#include "hci/acl_manager/acl_connection.h"
+#include "hci/acl_manager/connection_management_callbacks.h"
+#include "hci/hci_packets.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class ClassicAclConnection : public AclConnection {
+ public:
+  ClassicAclConnection();
+  ClassicAclConnection(std::shared_ptr<Queue> queue, AclConnectionInterface* acl_connection_interface, uint16_t handle,
+                       Address address);
+  ~ClassicAclConnection() override;
+
+  virtual Address GetAddress() const {
+    return address_;
+  }
+
+  virtual void RegisterCallbacks(ConnectionManagementCallbacks* callbacks, os::Handler* handler);
+  virtual bool Disconnect(DisconnectReason reason);
+  virtual bool ChangeConnectionPacketType(uint16_t packet_type);
+  virtual bool AuthenticationRequested();
+  virtual bool SetConnectionEncryption(Enable enable);
+  virtual bool ChangeConnectionLinkKey();
+  virtual bool ReadClockOffset();
+  virtual bool HoldMode(uint16_t max_interval, uint16_t min_interval);
+  virtual bool SniffMode(uint16_t max_interval, uint16_t min_interval, uint16_t attempt, uint16_t timeout);
+  virtual bool ExitSniffMode();
+  virtual bool QosSetup(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
+                        uint32_t delay_variation);
+  virtual bool RoleDiscovery();
+  virtual bool ReadLinkPolicySettings();
+  virtual bool WriteLinkPolicySettings(uint16_t link_policy_settings);
+  virtual bool FlowSpecification(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                                 uint32_t token_bucket_size, uint32_t peak_bandwidth, uint32_t access_latency);
+  virtual bool SniffSubrating(uint16_t maximum_latency, uint16_t minimum_remote_timeout,
+                              uint16_t minimum_local_timeout);
+  virtual bool Flush();
+  virtual bool ReadAutomaticFlushTimeout();
+  virtual bool WriteAutomaticFlushTimeout(uint16_t flush_timeout);
+  virtual bool ReadTransmitPowerLevel(TransmitPowerLevelType type);
+  virtual bool ReadLinkSupervisionTimeout();
+  virtual bool WriteLinkSupervisionTimeout(uint16_t link_supervision_timeout);
+  virtual bool ReadFailedContactCounter();
+  virtual bool ResetFailedContactCounter();
+  virtual bool ReadLinkQuality();
+  virtual bool ReadAfhChannelMap();
+  virtual bool ReadRssi();
+  virtual bool ReadClock(WhichClock which_clock);
+  virtual bool ReadRemoteVersionInformation();
+  virtual bool ReadRemoteSupportedFeatures();
+  virtual bool ReadRemoteExtendedFeatures();
+
+  // Called once before passing the connection to the client
+  virtual ConnectionManagementCallbacks* GetEventCallbacks();
+
+ private:
+  AclConnectionInterface* acl_connection_interface_;
+  Address address_;
+  struct impl;
+  struct impl* pimpl_ = nullptr;
+  DISALLOW_COPY_AND_ASSIGN(ClassicAclConnection);
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/classic_impl.h b/gd/hci/acl_manager/classic_impl.h
new file mode 100644
index 0000000..d78c6ad
--- /dev/null
+++ b/gd/hci/acl_manager/classic_impl.h
@@ -0,0 +1,550 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "common/bind.h"
+#include "hci/acl_manager/assembler.h"
+#include "hci/acl_manager/disconnector_for_le.h"
+#include "hci/acl_manager/event_checkers.h"
+#include "hci/acl_manager/round_robin_scheduler.h"
+#include "hci/controller.h"
+#include "security/security_manager_listener.h"
+#include "security/security_module.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+struct acl_connection {
+  acl_connection(AddressWithType address_with_type, AclConnection::QueueDownEnd* queue_down_end, os::Handler* handler)
+      : assembler_(address_with_type, queue_down_end, handler), address_with_type_(address_with_type) {}
+  ~acl_connection() = default;
+  struct acl_manager::assembler assembler_;
+  AddressWithType address_with_type_;
+  ConnectionManagementCallbacks* connection_management_callbacks_ = nullptr;
+};
+
+struct classic_impl : public DisconnectorForLe, public security::ISecurityManagerListener {
+  classic_impl(HciLayer* hci_layer, Controller* controller, os::Handler* handler,
+               RoundRobinScheduler* round_robin_scheduler)
+      : hci_layer_(hci_layer), controller_(controller), round_robin_scheduler_(round_robin_scheduler) {
+    hci_layer_ = hci_layer;
+    controller_ = controller;
+    handler_ = handler;
+    should_accept_connection_ = common::Bind([](Address, ClassOfDevice) { return true; });
+    acl_connection_interface_ =
+        hci_layer_->GetAclConnectionInterface(handler_->BindOn(this, &classic_impl::on_classic_event),
+                                              handler_->BindOn(this, &classic_impl::on_classic_disconnect));
+  }
+
+  ~classic_impl() override {
+    for (auto event_code : AclConnectionEvents) {
+      hci_layer_->UnregisterEventHandler(event_code);
+    }
+    acl_connections_.clear();
+    security_manager_.reset();
+  }
+
+  void on_classic_event(EventPacketView event_packet) {
+    EventCode event_code = event_packet.GetEventCode();
+    switch (event_code) {
+      case EventCode::CONNECTION_COMPLETE:
+        on_connection_complete(event_packet);
+        break;
+      case EventCode::CONNECTION_REQUEST:
+        on_incoming_connection(event_packet);
+        break;
+      case EventCode::CONNECTION_PACKET_TYPE_CHANGED:
+        on_connection_packet_type_changed(event_packet);
+        break;
+      case EventCode::AUTHENTICATION_COMPLETE:
+        on_authentication_complete(event_packet);
+        break;
+      case EventCode::READ_CLOCK_OFFSET_COMPLETE:
+        on_read_clock_offset_complete(event_packet);
+        break;
+      case EventCode::MODE_CHANGE:
+        on_mode_change(event_packet);
+        break;
+      case EventCode::QOS_SETUP_COMPLETE:
+        on_qos_setup_complete(event_packet);
+        break;
+      case EventCode::ROLE_CHANGE:
+        on_role_change(event_packet);
+        break;
+      case EventCode::FLOW_SPECIFICATION_COMPLETE:
+        on_flow_specification_complete(event_packet);
+        break;
+      case EventCode::FLUSH_OCCURRED:
+        on_flush_occurred(event_packet);
+        break;
+      case EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE:
+        on_read_remote_supported_features_complete(event_packet);
+        break;
+      case EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE:
+        on_read_remote_extended_features_complete(event_packet);
+        break;
+      case EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE:
+        on_read_remote_version_information_complete(event_packet);
+        break;
+      case EventCode::LINK_SUPERVISION_TIMEOUT_CHANGED:
+        on_link_supervision_timeout_changed(event_packet);
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unhandled event code %s", EventCodeText(event_code).c_str());
+    }
+  }
+
+  void on_classic_disconnect(uint16_t handle, ErrorCode reason) {
+    if (acl_connections_.count(handle) == 1) {
+      auto& connection = acl_connections_.find(handle)->second;
+      round_robin_scheduler_->Unregister(handle);
+      connection.connection_management_callbacks_->OnDisconnection(reason);
+      acl_connections_.erase(handle);
+    }
+  }
+
+  void handle_register_callbacks(ConnectionCallbacks* callbacks, os::Handler* handler) {
+    ASSERT(client_callbacks_ == nullptr);
+    ASSERT(client_handler_ == nullptr);
+    client_callbacks_ = callbacks;
+    client_handler_ = handler;
+  }
+
+  void handle_disconnect(uint16_t handle, DisconnectReason reason) {
+    acl_connection_interface_->EnqueueCommand(hci::DisconnectBuilder::Create(handle, reason),
+                                              handler_->BindOnce(&check_command_status<DisconnectStatusView>));
+  }
+
+  void on_incoming_connection(EventPacketView packet) {
+    ConnectionRequestView request = ConnectionRequestView::Create(packet);
+    ASSERT(request.IsValid());
+    Address address = request.GetBdAddr();
+    if (client_callbacks_ == nullptr) {
+      LOG_ERROR("No callbacks to call");
+      auto reason = RejectConnectionReason::LIMITED_RESOURCES;
+      this->reject_connection(RejectConnectionRequestBuilder::Create(address, reason));
+      return;
+    }
+    incoming_connecting_address_ = address;
+    if (is_classic_link_already_connected(address)) {
+      auto reason = RejectConnectionReason::UNACCEPTABLE_BD_ADDR;
+      this->reject_connection(RejectConnectionRequestBuilder::Create(address, reason));
+    } else if (should_accept_connection_.Run(address, request.GetClassOfDevice())) {
+      this->accept_connection(address);
+    } else {
+      auto reason = RejectConnectionReason::LIMITED_RESOURCES;  // TODO: determine reason
+      this->reject_connection(RejectConnectionRequestBuilder::Create(address, reason));
+    }
+  }
+
+  bool is_classic_link_already_connected(Address address) {
+    for (const auto& connection : acl_connections_) {
+      if (connection.second.address_with_type_.GetAddress() == address) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void create_connection(Address address) {
+    // TODO: Configure default connection parameters?
+    uint16_t packet_type = 0x4408 /* DM 1,3,5 */ | 0x8810 /*DH 1,3,5 */;
+    PageScanRepetitionMode page_scan_repetition_mode = PageScanRepetitionMode::R1;
+    uint16_t clock_offset = 0;
+    ClockOffsetValid clock_offset_valid = ClockOffsetValid::INVALID;
+    CreateConnectionRoleSwitch allow_role_switch = CreateConnectionRoleSwitch::ALLOW_ROLE_SWITCH;
+    ASSERT(client_callbacks_ != nullptr);
+    std::unique_ptr<CreateConnectionBuilder> packet = CreateConnectionBuilder::Create(
+        address, packet_type, page_scan_repetition_mode, clock_offset, clock_offset_valid, allow_role_switch);
+
+    if (incoming_connecting_address_ == Address::kEmpty && outgoing_connecting_address_ == Address::kEmpty) {
+      if (is_classic_link_already_connected(address)) {
+        LOG_WARN("already connected: %s", address.ToString().c_str());
+        return;
+      }
+      outgoing_connecting_address_ = address;
+      acl_connection_interface_->EnqueueCommand(std::move(packet), handler_->BindOnce([](CommandStatusView status) {
+        ASSERT(status.IsValid());
+        ASSERT(status.GetCommandOpCode() == OpCode::CREATE_CONNECTION);
+      }));
+    } else {
+      pending_outgoing_connections_.emplace(address, std::move(packet));
+    }
+  }
+
+  void on_connection_complete(EventPacketView packet) {
+    ConnectionCompleteView connection_complete = ConnectionCompleteView::Create(packet);
+    ASSERT(connection_complete.IsValid());
+    auto status = connection_complete.GetStatus();
+    auto address = connection_complete.GetBdAddr();
+    Role current_role = Role::MASTER;
+    if (outgoing_connecting_address_ == address) {
+      outgoing_connecting_address_ = Address::kEmpty;
+    } else {
+      ASSERT_LOG(incoming_connecting_address_ == address, "No prior connection request for %s",
+                 address.ToString().c_str());
+      incoming_connecting_address_ = Address::kEmpty;
+      current_role = Role::SLAVE;
+    }
+    if (status != ErrorCode::SUCCESS) {
+      client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectFail, common::Unretained(client_callbacks_),
+                                             address, status));
+      return;
+    }
+    uint16_t handle = connection_complete.GetConnectionHandle();
+    ASSERT(acl_connections_.count(handle) == 0);
+    auto queue = std::make_shared<AclConnection::Queue>(10);
+    acl_connections_.emplace(std::piecewise_construct, std::forward_as_tuple(handle),
+                             std::forward_as_tuple(AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS},
+                                                   queue->GetDownEnd(), handler_));
+    round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue);
+    std::unique_ptr<ClassicAclConnection> connection(
+        new ClassicAclConnection(std::move(queue), acl_connection_interface_, handle, address));
+    auto& connection_proxy = check_and_get_connection(handle);
+    connection_proxy.connection_management_callbacks_ = connection->GetEventCallbacks();
+    connection_proxy.connection_management_callbacks_->OnRoleChange(current_role);
+    client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectSuccess,
+                                           common::Unretained(client_callbacks_), std::move(connection)));
+    while (!pending_outgoing_connections_.empty()) {
+      auto create_connection_packet_and_address = std::move(pending_outgoing_connections_.front());
+      pending_outgoing_connections_.pop();
+      if (!is_classic_link_already_connected(create_connection_packet_and_address.first)) {
+        outgoing_connecting_address_ = create_connection_packet_and_address.first;
+        acl_connection_interface_->EnqueueCommand(std::move(create_connection_packet_and_address.second),
+                                                  handler_->BindOnce([](CommandStatusView status) {
+                                                    ASSERT(status.IsValid());
+                                                    ASSERT(status.GetCommandOpCode() == OpCode::CREATE_CONNECTION);
+                                                  }));
+        break;
+      }
+    }
+  }
+
+  void on_connection_packet_type_changed(EventPacketView packet) {
+    ConnectionPacketTypeChangedView packet_type_changed = ConnectionPacketTypeChangedView::Create(packet);
+    if (!packet_type_changed.IsValid()) {
+      LOG_ERROR("Received on_connection_packet_type_changed with invalid packet");
+      return;
+    } else if (packet_type_changed.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = packet_type_changed.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_connection_packet_type_changed with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = packet_type_changed.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    uint16_t packet_type = packet_type_changed.GetPacketType();
+    acl_connection.connection_management_callbacks_->OnConnectionPacketTypeChanged(packet_type);
+  }
+
+  void on_master_link_key_complete(EventPacketView packet) {
+    MasterLinkKeyCompleteView complete_view = MasterLinkKeyCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_master_link_key_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_master_link_key_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    KeyFlag key_flag = complete_view.GetKeyFlag();
+    acl_connection.connection_management_callbacks_->OnMasterLinkKeyComplete(key_flag);
+  }
+
+  void on_authentication_complete(EventPacketView packet) {
+    AuthenticationCompleteView authentication_complete = AuthenticationCompleteView::Create(packet);
+    if (!authentication_complete.IsValid()) {
+      LOG_ERROR("Received on_authentication_complete with invalid packet");
+      return;
+    } else if (authentication_complete.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = authentication_complete.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_authentication_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = authentication_complete.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    acl_connection.connection_management_callbacks_->OnAuthenticationComplete();
+  }
+
+  void cancel_connect(Address address) {
+    if (outgoing_connecting_address_ == address) {
+      LOG_INFO("Cannot cancel non-existent connection to %s", address.ToString().c_str());
+      return;
+    }
+    std::unique_ptr<CreateConnectionCancelBuilder> packet = CreateConnectionCancelBuilder::Create(address);
+    acl_connection_interface_->EnqueueCommand(
+        std::move(packet), handler_->BindOnce(&check_command_complete<CreateConnectionCancelCompleteView>));
+  }
+
+  void master_link_key(KeyFlag key_flag) {
+    std::unique_ptr<MasterLinkKeyBuilder> packet = MasterLinkKeyBuilder::Create(key_flag);
+    acl_connection_interface_->EnqueueCommand(std::move(packet),
+                                              handler_->BindOnce(&check_command_status<MasterLinkKeyStatusView>));
+  }
+
+  void switch_role(Address address, Role role) {
+    std::unique_ptr<SwitchRoleBuilder> packet = SwitchRoleBuilder::Create(address, role);
+    acl_connection_interface_->EnqueueCommand(std::move(packet),
+                                              handler_->BindOnce(&check_command_status<SwitchRoleStatusView>));
+  }
+
+  void write_default_link_policy_settings(uint16_t default_link_policy_settings) {
+    std::unique_ptr<WriteDefaultLinkPolicySettingsBuilder> packet =
+        WriteDefaultLinkPolicySettingsBuilder::Create(default_link_policy_settings);
+    acl_connection_interface_->EnqueueCommand(
+        std::move(packet), handler_->BindOnce(&check_command_complete<WriteDefaultLinkPolicySettingsCompleteView>));
+  }
+
+  void accept_connection(Address address) {
+    auto role = AcceptConnectionRequestRole::BECOME_MASTER;  // We prefer to be master
+    acl_connection_interface_->EnqueueCommand(
+        AcceptConnectionRequestBuilder::Create(address, role),
+        handler_->BindOnceOn(this, &classic_impl::on_accept_connection_status, address));
+  }
+
+  void on_change_connection_link_key_complete(EventPacketView packet) {
+    ChangeConnectionLinkKeyCompleteView complete_view = ChangeConnectionLinkKeyCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_change_connection_link_key_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_change_connection_link_key_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    acl_connection.connection_management_callbacks_->OnChangeConnectionLinkKeyComplete();
+  }
+
+  void on_read_clock_offset_complete(EventPacketView packet) {
+    ReadClockOffsetCompleteView complete_view = ReadClockOffsetCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_clock_offset_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_clock_offset_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    uint16_t clock_offset = complete_view.GetClockOffset();
+    acl_connection.connection_management_callbacks_->OnReadClockOffsetComplete(clock_offset);
+  }
+
+  void on_mode_change(EventPacketView packet) {
+    ModeChangeView mode_change_view = ModeChangeView::Create(packet);
+    if (!mode_change_view.IsValid()) {
+      LOG_ERROR("Received on_mode_change with invalid packet");
+      return;
+    } else if (mode_change_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = mode_change_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_mode_change with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = mode_change_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    Mode current_mode = mode_change_view.GetCurrentMode();
+    uint16_t interval = mode_change_view.GetInterval();
+    acl_connection.connection_management_callbacks_->OnModeChange(current_mode, interval);
+  }
+
+  void on_qos_setup_complete(EventPacketView packet) {
+    QosSetupCompleteView complete_view = QosSetupCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_qos_setup_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_qos_setup_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    ServiceType service_type = complete_view.GetServiceType();
+    uint32_t token_rate = complete_view.GetTokenRate();
+    uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
+    uint32_t latency = complete_view.GetLatency();
+    uint32_t delay_variation = complete_view.GetDelayVariation();
+    acl_connection.connection_management_callbacks_->OnQosSetupComplete(service_type, token_rate, peak_bandwidth,
+                                                                        latency, delay_variation);
+  }
+
+  void on_role_change(EventPacketView packet) {
+    RoleChangeView role_change_view = RoleChangeView::Create(packet);
+    if (!role_change_view.IsValid()) {
+      LOG_ERROR("Received on_role_change with invalid packet");
+      return;
+    } else if (role_change_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = role_change_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_role_change with error code %s", error_code.c_str());
+      return;
+    }
+    Address bd_addr = role_change_view.GetBdAddr();
+    Role new_role = role_change_view.GetNewRole();
+    for (auto& connection_pair : acl_connections_) {
+      if (connection_pair.second.address_with_type_.GetAddress() == bd_addr) {
+        connection_pair.second.connection_management_callbacks_->OnRoleChange(new_role);
+      }
+    }
+  }
+
+  void on_flow_specification_complete(EventPacketView packet) {
+    FlowSpecificationCompleteView complete_view = FlowSpecificationCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_flow_specification_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_flow_specification_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    FlowDirection flow_direction = complete_view.GetFlowDirection();
+    ServiceType service_type = complete_view.GetServiceType();
+    uint32_t token_rate = complete_view.GetTokenRate();
+    uint32_t token_bucket_size = complete_view.GetTokenBucketSize();
+    uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
+    uint32_t access_latency = complete_view.GetAccessLatency();
+    acl_connection.connection_management_callbacks_->OnFlowSpecificationComplete(
+        flow_direction, service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
+  }
+
+  void on_flush_occurred(EventPacketView packet) {
+    FlushOccurredView flush_occurred_view = FlushOccurredView::Create(packet);
+    if (!flush_occurred_view.IsValid()) {
+      LOG_ERROR("Received on_flush_occurred with invalid packet");
+      return;
+    }
+    uint16_t handle = flush_occurred_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    acl_connection.connection_management_callbacks_->OnFlushOccurred();
+  }
+
+  void on_read_remote_version_information_complete(EventPacketView packet) {
+    auto view = ReadRemoteVersionInformationCompleteView::Create(packet);
+    ASSERT_LOG(view.IsValid(), "Read remote version information packet invalid");
+    LOG_INFO("UNIMPLEMENTED called");
+  }
+
+  void on_read_remote_supported_features_complete(EventPacketView packet) {
+    auto view = ReadRemoteSupportedFeaturesCompleteView::Create(packet);
+    ASSERT_LOG(view.IsValid(), "Read remote supported features packet invalid");
+    LOG_INFO("UNIMPLEMENTED called");
+  }
+
+  void on_read_remote_extended_features_complete(EventPacketView packet) {
+    auto view = ReadRemoteExtendedFeaturesCompleteView::Create(packet);
+    ASSERT_LOG(view.IsValid(), "Read remote extended features packet invalid");
+    LOG_INFO("UNIMPLEMENTED called");
+  }
+
+  void on_link_supervision_timeout_changed(EventPacketView packet) {
+    auto view = LinkSupervisionTimeoutChangedView::Create(packet);
+    ASSERT_LOG(view.IsValid(), "Link supervision timeout changed packet invalid");
+    LOG_INFO("UNIMPLEMENTED called");
+  }
+
+  void on_accept_connection_status(Address address, CommandStatusView status) {
+    auto accept_status = AcceptConnectionRequestStatusView::Create(status);
+    ASSERT(accept_status.IsValid());
+    if (status.GetStatus() != ErrorCode::SUCCESS) {
+      cancel_connect(address);
+    }
+  }
+
+  void reject_connection(std::unique_ptr<RejectConnectionRequestBuilder> builder) {
+    acl_connection_interface_->EnqueueCommand(
+        std::move(builder), handler_->BindOnce(&check_command_status<RejectConnectionRequestStatusView>));
+  }
+
+  acl_connection& check_and_get_connection(uint16_t handle) {
+    auto connection = acl_connections_.find(handle);
+    ASSERT(connection != acl_connections_.end());
+    return connection->second;
+  }
+
+  void OnDeviceBonded(bluetooth::hci::AddressWithType device) override {}
+  void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) override {}
+  void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) override {}
+
+  void OnEncryptionStateChanged(EncryptionChangeView encryption_change_view) override {
+    if (!encryption_change_view.IsValid()) {
+      LOG_ERROR("Invalid packet");
+      return;
+    } else if (encryption_change_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = encryption_change_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("error_code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = encryption_change_view.GetConnectionHandle();
+    auto acl_connection = acl_connections_.find(handle);
+    if (acl_connection == acl_connections_.end()) {
+      LOG_INFO("Invalid handle (already closed?) %d", handle);
+      return;
+    }
+    EncryptionEnabled enabled = encryption_change_view.GetEncryptionEnabled();
+    acl_connection->second.connection_management_callbacks_->OnEncryptionChange(enabled);
+  }
+
+  void set_security_module(security::SecurityModule* security_module) {
+    security_manager_ = security_module->GetSecurityManager();
+    security_manager_->RegisterCallbackListener(this, handler_);
+  }
+
+  uint16_t HACK_get_handle(Address address) {
+    for (auto it = acl_connections_.begin(); it != acl_connections_.end(); it++) {
+      if (it->second.address_with_type_.GetAddress() == address) {
+        return it->first;
+      }
+    }
+    return 0xFFFF;
+  }
+
+  HciLayer* hci_layer_ = nullptr;
+  Controller* controller_ = nullptr;
+  RoundRobinScheduler* round_robin_scheduler_ = nullptr;
+  AclConnectionInterface* acl_connection_interface_ = nullptr;
+  os::Handler* handler_ = nullptr;
+  ConnectionCallbacks* client_callbacks_ = nullptr;
+  os::Handler* client_handler_ = nullptr;
+  std::map<uint16_t, acl_connection> acl_connections_;
+  Address outgoing_connecting_address_{Address::kEmpty};
+  Address incoming_connecting_address_{Address::kEmpty};
+  common::Callback<bool(Address, ClassOfDevice)> should_accept_connection_;
+  std::queue<std::pair<Address, std::unique_ptr<CreateConnectionBuilder>>> pending_outgoing_connections_;
+
+  std::unique_ptr<security::SecurityManager> security_manager_;
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/connection_callbacks.h b/gd/hci/acl_manager/connection_callbacks.h
new file mode 100644
index 0000000..0f9473d
--- /dev/null
+++ b/gd/hci/acl_manager/connection_callbacks.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include "hci/acl_manager/classic_acl_connection.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class ConnectionCallbacks {
+ public:
+  virtual ~ConnectionCallbacks() = default;
+  // Invoked when controller sends Connection Complete event with Success error code
+  virtual void OnConnectSuccess(std::unique_ptr<ClassicAclConnection>) = 0;
+  // Invoked when controller sends Connection Complete event with non-Success error code
+  virtual void OnConnectFail(Address, ErrorCode reason) = 0;
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/connection_management_callbacks.h b/gd/hci/acl_manager/connection_management_callbacks.h
new file mode 100644
index 0000000..6fafab8
--- /dev/null
+++ b/gd/hci/acl_manager/connection_management_callbacks.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include "hci/hci_packets.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class ConnectionManagementCallbacks {
+ public:
+  virtual ~ConnectionManagementCallbacks() = default;
+  // Invoked when controller sends Connection Packet Type Changed event with Success error code
+  virtual void OnConnectionPacketTypeChanged(uint16_t packet_type) = 0;
+  // Invoked when controller sends Authentication Complete event with Success error code
+  virtual void OnAuthenticationComplete() = 0;
+  // Invoked when controller sends Encryption Change event with Success error code
+  virtual void OnEncryptionChange(EncryptionEnabled enabled) = 0;
+  // Invoked when controller sends Change Connection Link Key Complete event with Success error code
+  virtual void OnChangeConnectionLinkKeyComplete() = 0;
+  // Invoked when controller sends Read Clock Offset Complete event with Success error code
+  virtual void OnReadClockOffsetComplete(uint16_t clock_offset) = 0;
+  // Invoked when controller sends Mode Change event with Success error code
+  virtual void OnModeChange(Mode current_mode, uint16_t interval) = 0;
+  // Invoked when controller sends QoS Setup Complete event with Success error code
+  virtual void OnQosSetupComplete(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
+                                  uint32_t latency, uint32_t delay_variation) = 0;
+  // Invoked when controller sends Flow Specification Complete event with Success error code
+  virtual void OnFlowSpecificationComplete(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                                           uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                           uint32_t access_latency) = 0;
+  // Invoked when controller sends Flush Occurred event
+  virtual void OnFlushOccurred() = 0;
+  // Invoked when controller sends Command Complete event for Role Discovery command with Success error code
+  virtual void OnRoleDiscoveryComplete(Role current_role) = 0;
+  // Invoked when controller sends Command Complete event for Read Link Policy Settings command with Success error code
+  virtual void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) = 0;
+  // Invoked when controller sends Command Complete event for Read Automatic Flush Timeout command with Success error
+  // code
+  virtual void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) = 0;
+  // Invoked when controller sends Command Complete event for Read Transmit Power Level command with Success error code
+  virtual void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) = 0;
+  // Invoked when controller sends Command Complete event for Read Link Supervision Time out command with Success error
+  // code
+  virtual void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) = 0;
+  // Invoked when controller sends Command Complete event for Read Failed Contact Counter command with Success error
+  // code
+  virtual void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) = 0;
+  // Invoked when controller sends Command Complete event for Read Link Quality command with Success error code
+  virtual void OnReadLinkQualityComplete(uint8_t link_quality) = 0;
+  // Invoked when controller sends Command Complete event for Read AFH Channel Map command with Success error code
+  virtual void OnReadAfhChannelMapComplete(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) = 0;
+  // Invoked when controller sends Command Complete event for Read RSSI command with Success error code
+  virtual void OnReadRssiComplete(uint8_t rssi) = 0;
+  // Invoked when controller sends Command Complete event for Read Clock command with Success error code
+  virtual void OnReadClockComplete(uint32_t clock, uint16_t accuracy) = 0;
+  // Invoked when controller sends Master Link Key Complete event
+  virtual void OnMasterLinkKeyComplete(KeyFlag key_flag) = 0;
+  // Invoked when controller sends Role Change event
+  virtual void OnRoleChange(Role new_role) = 0;
+  // Invoked when controller sends DisconnectComplete
+  virtual void OnDisconnection(ErrorCode reason) = 0;
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/shim/iconnectability.h b/gd/hci/acl_manager/disconnector_for_le.h
similarity index 63%
rename from gd/shim/iconnectability.h
rename to gd/hci/acl_manager/disconnector_for_le.h
index fbfaaa3..cdb21c2 100644
--- a/gd/shim/iconnectability.h
+++ b/gd/hci/acl_manager/disconnector_for_le.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,21 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
-/**
- * The gd API exported to the legacy api
- */
+#include "hci/hci_packets.h"
+
 namespace bluetooth {
-namespace shim {
+namespace hci {
+namespace acl_manager {
 
-struct IConnectability {
-  virtual void StartConnectability() = 0;
-  virtual void StopConnectability() = 0;
-  virtual bool IsConnectable() const = 0;
-
-  virtual ~IConnectability() {}
+class DisconnectorForLe {
+ public:
+  DisconnectorForLe() = default;
+  virtual ~DisconnectorForLe() = default;
+  virtual void handle_disconnect(uint16_t handle, DisconnectReason reason) = 0;
 };
 
-}  // namespace shim
+}  // namespace acl_manager
+}  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/acl_manager/event_checkers.h b/gd/hci/acl_manager/event_checkers.h
new file mode 100644
index 0000000..1765661
--- /dev/null
+++ b/gd/hci/acl_manager/event_checkers.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+template <class T>
+void check_command_complete(CommandCompleteView view) {
+  ASSERT(view.IsValid());
+  auto status_view = T::Create(view);
+  if (!status_view.IsValid()) {
+    LOG_ERROR("Received command complete with invalid packet, opcode 0x%02hx", view.GetCommandOpCode());
+    return;
+  }
+  ErrorCode status = status_view.GetStatus();
+  OpCode op_code = status_view.GetCommandOpCode();
+  if (status != ErrorCode::SUCCESS) {
+    std::string error_code = ErrorCodeText(status);
+    LOG_ERROR("Received command complete with error code %s, opcode 0x%02hx", error_code.c_str(), op_code);
+    return;
+  }
+}
+
+template <class T>
+void check_command_status(CommandStatusView view) {
+  ASSERT(view.IsValid());
+  auto status_view = T::Create(view);
+  if (!status_view.IsValid()) {
+    LOG_ERROR("Received command status with invalid packet, opcode 0x%02hx", view.GetCommandOpCode());
+    return;
+  }
+  ErrorCode status = status_view.GetStatus();
+  OpCode op_code = status_view.GetCommandOpCode();
+  if (status != ErrorCode::SUCCESS) {
+    std::string error_code = ErrorCodeText(status);
+    LOG_ERROR("Received command status with error code %s, opcode 0x%02hx", error_code.c_str(), op_code);
+    return;
+  }
+}
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/le_acl_connection.cc b/gd/hci/acl_manager/le_acl_connection.cc
new file mode 100644
index 0000000..42cf91e
--- /dev/null
+++ b/gd/hci/acl_manager/le_acl_connection.cc
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+#include "hci/acl_manager/le_acl_connection.h"
+#include "hci/acl_manager/le_connection_management_callbacks.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class LeAclConnectionTracker : public LeConnectionManagementCallbacks {
+ public:
+  LeAclConnectionTracker(LeAclConnectionInterface* le_acl_connection_interface,
+                         common::OnceCallback<void(DisconnectReason reason)> disconnect)
+      : le_acl_connection_interface_(le_acl_connection_interface), do_disconnect_(std::move(disconnect)) {}
+  ~LeAclConnectionTracker() override {
+    ASSERT(queued_callbacks_.empty());
+  }
+  void RegisterCallbacks(LeConnectionManagementCallbacks* callbacks, os::Handler* handler) {
+    while (!queued_callbacks_.empty()) {
+      auto iter = queued_callbacks_.begin();
+      handler->Post(std::move(*iter));
+      queued_callbacks_.erase(iter);
+    }
+    client_handler_ = handler;
+    client_callbacks_ = callbacks;
+  }
+
+#define SAVE_OR_CALL(f, ...)                                                                                        \
+  if (client_handler_ == nullptr) {                                                                                 \
+    queued_callbacks_.emplace_back(                                                                                 \
+        common::BindOnce(&LeConnectionManagementCallbacks::f, common::Unretained(this), __VA_ARGS__));              \
+  } else {                                                                                                          \
+    client_handler_->Post(                                                                                          \
+        common::BindOnce(&LeConnectionManagementCallbacks::f, common::Unretained(client_callbacks_), __VA_ARGS__)); \
+  }
+
+  void OnConnectionUpdate(uint16_t conn_interval, uint16_t conn_latency, uint16_t supervision_timeout) override {
+    SAVE_OR_CALL(OnConnectionUpdate, conn_interval, conn_latency, supervision_timeout)
+  }
+
+  void OnDisconnection(ErrorCode reason) override {
+    SAVE_OR_CALL(OnDisconnection, reason);
+  }
+#undef SAVE_OR_CALL
+
+  LeAclConnectionInterface* le_acl_connection_interface_;
+  common::OnceCallback<void(DisconnectReason)> do_disconnect_;
+  os::Handler* client_handler_ = nullptr;
+  LeConnectionManagementCallbacks* client_callbacks_ = nullptr;
+  std::list<common::OnceClosure> queued_callbacks_;
+};
+
+struct LeAclConnection::impl {
+  impl(LeAclConnectionInterface* le_acl_connection_interface, std::shared_ptr<Queue> queue,
+       common::OnceCallback<void(DisconnectReason)> disconnect)
+      : queue_(std::move(queue)), tracker(le_acl_connection_interface, std::move(disconnect)) {}
+  LeConnectionManagementCallbacks* GetEventCallbacks() {
+    ASSERT(!callbacks_given_);
+    callbacks_given_ = true;
+    return &tracker;
+  }
+
+  bool callbacks_given_{false};
+  std::shared_ptr<Queue> queue_;
+  LeAclConnectionTracker tracker;
+};
+
+LeAclConnection::LeAclConnection()
+    : AclConnection(), local_address_(Address::kEmpty, AddressType::PUBLIC_DEVICE_ADDRESS),
+      remote_address_(Address::kEmpty, AddressType::PUBLIC_DEVICE_ADDRESS) {}
+
+LeAclConnection::LeAclConnection(std::shared_ptr<Queue> queue, LeAclConnectionInterface* le_acl_connection_interface,
+                                 common::OnceCallback<void(DisconnectReason)> disconnect, uint16_t handle,
+                                 AddressWithType local_address, AddressWithType remote_address, Role role)
+    : AclConnection(queue->GetUpEnd(), handle), local_address_(local_address), remote_address_(remote_address),
+      role_(role) {
+  pimpl_ = new LeAclConnection::impl(le_acl_connection_interface, std::move(queue), std::move(disconnect));
+}
+
+LeAclConnection::~LeAclConnection() {
+  delete pimpl_;
+  AclConnection::~AclConnection();
+}
+
+void LeAclConnection::RegisterCallbacks(LeConnectionManagementCallbacks* callbacks, os::Handler* handler) {
+  return pimpl_->tracker.RegisterCallbacks(callbacks, handler);
+}
+
+void LeAclConnection::Disconnect(DisconnectReason reason) {
+  common::BindOnce(std::move(pimpl_->tracker.do_disconnect_), reason).Run();
+}
+
+LeConnectionManagementCallbacks* LeAclConnection::GetEventCallbacks() {
+  return pimpl_->GetEventCallbacks();
+}
+
+bool LeAclConnection::LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
+                                         uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length) {
+  if (conn_interval_min < 0x0006 || conn_interval_min > 0x0C80 || conn_interval_max < 0x0006 ||
+      conn_interval_max > 0x0C80 || conn_latency > 0x01F3 || supervision_timeout < 0x000A ||
+      supervision_timeout > 0x0C80) {
+    LOG_ERROR("Invalid parameter");
+    return false;
+  }
+  pimpl_->tracker.le_acl_connection_interface_->EnqueueCommand(
+      LeConnectionUpdateBuilder::Create(handle_, conn_interval_min, conn_interval_max, conn_latency,
+                                        supervision_timeout, min_ce_length, max_ce_length),
+      pimpl_->tracker.client_handler_->BindOnce([](CommandStatusView status) {
+        ASSERT(status.IsValid());
+        ASSERT(status.GetCommandOpCode() == OpCode::LE_CONNECTION_UPDATE);
+      }));
+  return true;
+}
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/le_acl_connection.h b/gd/hci/acl_manager/le_acl_connection.h
new file mode 100644
index 0000000..626afd7
--- /dev/null
+++ b/gd/hci/acl_manager/le_acl_connection.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "hci/acl_manager/acl_connection.h"
+#include "hci/acl_manager/le_connection_management_callbacks.h"
+#include "hci/address_with_type.h"
+#include "hci/hci_packets.h"
+#include "hci/le_acl_connection_interface.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class LeAclConnection : public AclConnection {
+ public:
+  LeAclConnection();
+  LeAclConnection(std::shared_ptr<Queue> queue, LeAclConnectionInterface* le_acl_connection_interface,
+                  common::OnceCallback<void(DisconnectReason)> disconnect, uint16_t handle,
+                  AddressWithType local_address, AddressWithType remote_address, Role role);
+  ~LeAclConnection() override;
+
+  virtual AddressWithType GetLocalAddress() const {
+    return local_address_;
+  }
+
+  virtual AddressWithType GetRemoteAddress() const {
+    return remote_address_;
+  }
+
+  virtual Role GetRole() const {
+    return role_;
+  }
+
+  virtual void RegisterCallbacks(LeConnectionManagementCallbacks* callbacks, os::Handler* handler);
+  virtual void Disconnect(DisconnectReason reason);
+
+  virtual bool LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
+                                  uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length);
+
+  // Called once before passing the connection to the client
+  virtual LeConnectionManagementCallbacks* GetEventCallbacks();
+
+ private:
+  struct impl;
+  struct impl* pimpl_ = nullptr;
+  AddressWithType local_address_;
+  AddressWithType remote_address_;
+  Role role_;
+  DISALLOW_COPY_AND_ASSIGN(LeAclConnection);
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/le_connection_callbacks.h b/gd/hci/acl_manager/le_connection_callbacks.h
new file mode 100644
index 0000000..b79f16c
--- /dev/null
+++ b/gd/hci/acl_manager/le_connection_callbacks.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include "hci/acl_manager/le_acl_connection.h"
+#include "hci/address_with_type.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class LeConnectionCallbacks {
+ public:
+  virtual ~LeConnectionCallbacks() = default;
+  // Invoked when controller sends Connection Complete event with Success error code
+  // AddressWithType is always equal to the object used in AclManager#CreateLeConnection
+  virtual void OnLeConnectSuccess(AddressWithType, std::unique_ptr<LeAclConnection>) = 0;
+  // Invoked when controller sends Connection Complete event with non-Success error code
+  virtual void OnLeConnectFail(AddressWithType, ErrorCode reason) = 0;
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/le_connection_management_callbacks.h b/gd/hci/acl_manager/le_connection_management_callbacks.h
new file mode 100644
index 0000000..4b9fece
--- /dev/null
+++ b/gd/hci/acl_manager/le_connection_management_callbacks.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include "hci/hci_packets.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class LeConnectionManagementCallbacks {
+ public:
+  virtual ~LeConnectionManagementCallbacks() = default;
+  virtual void OnConnectionUpdate(uint16_t connection_interval, uint16_t connection_latency,
+                                  uint16_t supervision_timeout) = 0;
+  virtual void OnDisconnection(ErrorCode reason) = 0;
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/le_impl.h b/gd/hci/acl_manager/le_impl.h
new file mode 100644
index 0000000..669d5d9
--- /dev/null
+++ b/gd/hci/acl_manager/le_impl.h
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "common/bind.h"
+#include "crypto_toolbox/crypto_toolbox.h"
+#include "hci/acl_manager/assembler.h"
+#include "hci/acl_manager/disconnector_for_le.h"
+#include "hci/acl_manager/round_robin_scheduler.h"
+#include "hci/le_address_manager.h"
+#include "os/alarm.h"
+#include "os/rand.h"
+
+using bluetooth::crypto_toolbox::Octet16;
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+using common::BindOnce;
+
+struct le_acl_connection {
+  le_acl_connection(
+      AddressWithType address_with_type, AclConnection::QueueDownEnd* queue_down_end, os::Handler* handler)
+      : assembler_(address_with_type, queue_down_end, handler), address_with_type_(address_with_type) {}
+  ~le_acl_connection() = default;
+  struct acl_manager::assembler assembler_;
+  AddressWithType address_with_type_;
+  LeConnectionManagementCallbacks* le_connection_management_callbacks_ = nullptr;
+};
+
+struct le_impl : public bluetooth::hci::LeAddressManagerCallback {
+  le_impl(HciLayer* hci_layer, Controller* controller, os::Handler* handler, RoundRobinScheduler* round_robin_scheduler,
+          DisconnectorForLe* disconnector)
+      : hci_layer_(hci_layer), controller_(controller), round_robin_scheduler_(round_robin_scheduler),
+        disconnector_(disconnector) {
+    hci_layer_ = hci_layer;
+    controller_ = controller;
+    handler_ = handler;
+    le_acl_connection_interface_ = hci_layer_->GetLeAclConnectionInterface(
+        handler_->BindOn(this, &le_impl::on_le_event), handler_->BindOn(this, &le_impl::on_le_disconnect));
+    le_address_manager_ = new LeAddressManager(
+        common::Bind(&le_impl::enqueue_command, common::Unretained(this)),
+        handler_,
+        controller->GetControllerMacAddress(),
+        controller->GetControllerLeConnectListSize(),
+        controller->GetControllerLeResolvingListSize());
+  }
+
+  ~le_impl() {
+    for (auto subevent_code : LeConnectionManagementEvents) {
+      hci_layer_->UnregisterLeEventHandler(subevent_code);
+    }
+    if (address_manager_registered) {
+      le_address_manager_->Unregister(this);
+    }
+    delete le_address_manager_;
+    le_acl_connections_.clear();
+  }
+
+  void on_le_event(LeMetaEventView event_packet) {
+    SubeventCode code = event_packet.GetSubeventCode();
+    switch (code) {
+      case SubeventCode::CONNECTION_COMPLETE:
+        on_le_connection_complete(event_packet);
+        break;
+      case SubeventCode::ENHANCED_CONNECTION_COMPLETE:
+        on_le_enhanced_connection_complete(event_packet);
+        break;
+      case SubeventCode::CONNECTION_UPDATE_COMPLETE:
+        on_le_connection_update_complete(event_packet);
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unhandled event code %s", SubeventCodeText(code).c_str());
+    }
+  }
+
+  void on_le_disconnect(uint16_t handle, ErrorCode reason) {
+    if (le_acl_connections_.count(handle) == 1) {
+      auto& connection = le_acl_connections_.find(handle)->second;
+      round_robin_scheduler_->Unregister(handle);
+      connection.le_connection_management_callbacks_->OnDisconnection(reason);
+      le_acl_connections_.erase(handle);
+    }
+  }
+
+  void on_common_le_connection_complete(AddressWithType address_with_type) {
+    auto connecting_addr_with_type = connecting_le_.find(address_with_type);
+    if (connecting_addr_with_type == connecting_le_.end()) {
+      LOG_WARN("No prior connection request for %s", address_with_type.ToString().c_str());
+    } else {
+      connecting_le_.erase(connecting_addr_with_type);
+    }
+  }
+
+  void on_le_connection_complete(LeMetaEventView packet) {
+    LeConnectionCompleteView connection_complete = LeConnectionCompleteView::Create(packet);
+    ASSERT(connection_complete.IsValid());
+    auto status = connection_complete.GetStatus();
+    auto address = connection_complete.GetPeerAddress();
+    auto peer_address_type = connection_complete.GetPeerAddressType();
+    // TODO: find out which address and type was used to initiate the connection
+    AddressWithType remote_address(address, peer_address_type);
+    AddressWithType local_address = le_address_manager_->GetCurrentAddress();
+    on_common_le_connection_complete(remote_address);
+    if (status == ErrorCode::UNKNOWN_CONNECTION &&
+        canceled_connections_.find(remote_address) != canceled_connections_.end()) {
+      // connection canceled by LeAddressManager.OnPause(), will auto reconnect by LeAddressManager.OnResume()
+      return;
+    } else {
+      canceled_connections_.erase(remote_address);
+      remove_device_from_connect_list(remote_address);
+    }
+
+    if (status != ErrorCode::SUCCESS) {
+      check_for_unregister();
+      le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectFail,
+                                                common::Unretained(le_client_callbacks_), remote_address, status));
+      return;
+    }
+    // TODO: Check and save other connection parameters
+    auto role = connection_complete.GetRole();
+    uint16_t handle = connection_complete.GetConnectionHandle();
+    ASSERT(le_acl_connections_.count(handle) == 0);
+    auto queue = std::make_shared<AclConnection::Queue>(10);
+    le_acl_connections_.emplace(std::piecewise_construct, std::forward_as_tuple(handle),
+                                std::forward_as_tuple(remote_address, queue->GetDownEnd(), handler_));
+    auto& connection_proxy = check_and_get_le_connection(handle);
+    auto do_disconnect =
+        common::BindOnce(&DisconnectorForLe::handle_disconnect, common::Unretained(disconnector_), handle);
+    round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, handle, queue);
+    std::unique_ptr<LeAclConnection> connection(new LeAclConnection(std::move(queue), le_acl_connection_interface_,
+                                                                    std::move(do_disconnect), handle, local_address,
+                                                                    remote_address, role));
+    connection_proxy.le_connection_management_callbacks_ = connection->GetEventCallbacks();
+    le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
+                                              common::Unretained(le_client_callbacks_), remote_address,
+                                              std::move(connection)));
+  }
+
+  void on_le_enhanced_connection_complete(LeMetaEventView packet) {
+    LeEnhancedConnectionCompleteView connection_complete = LeEnhancedConnectionCompleteView::Create(packet);
+    ASSERT(connection_complete.IsValid());
+    auto status = connection_complete.GetStatus();
+    auto address = connection_complete.GetPeerAddress();
+    auto peer_address_type = connection_complete.GetPeerAddressType();
+    auto peer_resolvable_address = connection_complete.GetPeerResolvablePrivateAddress();
+    AddressWithType remote_address(address, peer_address_type);
+    AddressWithType local_address = le_address_manager_->GetCurrentAddress();
+    if (!peer_resolvable_address.IsEmpty()) {
+      remote_address = AddressWithType(peer_resolvable_address, AddressType::RANDOM_DEVICE_ADDRESS);
+    }
+    on_common_le_connection_complete(remote_address);
+    if (status == ErrorCode::UNKNOWN_CONNECTION &&
+        canceled_connections_.find(remote_address) != canceled_connections_.end()) {
+      // connection canceled by LeAddressManager.OnPause(), will auto reconnect by LeAddressManager.OnResume()
+      return;
+    } else {
+      canceled_connections_.erase(remote_address);
+      remove_device_from_connect_list(remote_address);
+    }
+
+    if (status != ErrorCode::SUCCESS) {
+      check_for_unregister();
+      le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectFail,
+                                                common::Unretained(le_client_callbacks_), remote_address, status));
+      return;
+    }
+    // TODO: Check and save other connection parameters
+    uint16_t handle = connection_complete.GetConnectionHandle();
+    ASSERT(le_acl_connections_.count(handle) == 0);
+    auto queue = std::make_shared<AclConnection::Queue>(10);
+    le_acl_connections_.emplace(std::piecewise_construct, std::forward_as_tuple(handle),
+                                std::forward_as_tuple(remote_address, queue->GetDownEnd(), handler_));
+    auto& connection_proxy = check_and_get_le_connection(handle);
+    round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, handle, queue);
+    auto role = connection_complete.GetRole();
+    auto do_disconnect =
+        common::BindOnce(&DisconnectorForLe::handle_disconnect, common::Unretained(disconnector_), handle);
+    std::unique_ptr<LeAclConnection> connection(new LeAclConnection(std::move(queue), le_acl_connection_interface_,
+                                                                    std::move(do_disconnect), handle, local_address,
+                                                                    remote_address, role));
+    connection_proxy.le_connection_management_callbacks_ = connection->GetEventCallbacks();
+    le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
+                                              common::Unretained(le_client_callbacks_), remote_address,
+                                              std::move(connection)));
+  }
+
+  void on_le_connection_update_complete(LeMetaEventView view) {
+    auto complete_view = LeConnectionUpdateCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_le_connection_update_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_le_connection_update_complete with error code %s", error_code.c_str());
+      return;
+    }
+    auto handle = complete_view.GetConnectionHandle();
+    if (le_acl_connections_.find(handle) == le_acl_connections_.end()) {
+      LOG_WARN("Can't find connection %hd", handle);
+      return;
+    }
+    auto& connection = le_acl_connections_.find(handle)->second;
+    connection.le_connection_management_callbacks_->OnConnectionUpdate(
+        complete_view.GetConnInterval(), complete_view.GetConnLatency(), complete_view.GetSupervisionTimeout());
+  }
+
+  void enqueue_command(std::unique_ptr<CommandPacketBuilder> command_packet) {
+    hci_layer_->EnqueueCommand(
+        std::move(command_packet),
+        handler_->BindOnce(&LeAddressManager::OnCommandComplete, common::Unretained(le_address_manager_)));
+  }
+
+  void add_device_to_connect_list(AddressWithType address_with_type) {
+    AddressType address_type = address_with_type.GetAddressType();
+    if (!address_manager_registered) {
+      le_address_manager_->Register(this);
+      address_manager_registered = true;
+    }
+    pause_connection = true;
+    switch (address_type) {
+      case AddressType::PUBLIC_DEVICE_ADDRESS:
+      case AddressType::PUBLIC_IDENTITY_ADDRESS: {
+        le_address_manager_->AddDeviceToConnectList(ConnectListAddressType::PUBLIC, address_with_type.GetAddress());
+      } break;
+      case AddressType::RANDOM_DEVICE_ADDRESS:
+      case AddressType::RANDOM_IDENTITY_ADDRESS: {
+        le_address_manager_->AddDeviceToConnectList(ConnectListAddressType::RANDOM, address_with_type.GetAddress());
+      }
+    }
+  }
+
+  void create_le_connection(AddressWithType address_with_type, bool add_to_connect_list) {
+    // TODO: Configure default LE connection parameters?
+
+    if (add_to_connect_list) {
+      add_device_to_connect_list(address_with_type);
+    }
+
+    if (!address_manager_registered) {
+      auto policy = le_address_manager_->Register(this);
+      address_manager_registered = true;
+
+      // Pause connection, wait for set random address complete
+      if (policy == LeAddressManager::AddressPolicy::USE_RESOLVABLE_ADDRESS ||
+          policy == LeAddressManager::AddressPolicy::USE_NON_RESOLVABLE_ADDRESS) {
+        pause_connection = true;
+      }
+    }
+
+    if (pause_connection) {
+      canceled_connections_.insert(address_with_type);
+      return;
+    }
+
+    uint16_t le_scan_interval = 0x0060;
+    uint16_t le_scan_window = 0x0030;
+    InitiatorFilterPolicy initiator_filter_policy = InitiatorFilterPolicy::USE_CONNECT_LIST;
+    OwnAddressType own_address_type =
+        static_cast<OwnAddressType>(le_address_manager_->GetCurrentAddress().GetAddressType());
+    uint16_t conn_interval_min = 0x0018;
+    uint16_t conn_interval_max = 0x0028;
+    uint16_t conn_latency = 0x0000;
+    uint16_t supervision_timeout = 0x001f4;
+    ASSERT(le_client_callbacks_ != nullptr);
+
+    connecting_le_.insert(address_with_type);
+
+    // TODO: make features check nicer, like HCI_LE_EXTENDED_ADVERTISING_SUPPORTED
+    if (controller_->GetControllerLeLocalSupportedFeatures() & 0x0010) {
+      LeCreateConnPhyScanParameters tmp;
+      tmp.scan_interval_ = le_scan_interval;
+      tmp.scan_window_ = le_scan_window;
+      tmp.conn_interval_min_ = conn_interval_min;
+      tmp.conn_interval_max_ = conn_interval_max;
+      tmp.conn_latency_ = conn_latency;
+      tmp.supervision_timeout_ = supervision_timeout;
+      tmp.min_ce_length_ = 0x00;
+      tmp.max_ce_length_ = 0x00;
+
+      le_acl_connection_interface_->EnqueueCommand(
+          LeExtendedCreateConnectionBuilder::Create(initiator_filter_policy, own_address_type,
+                                                    address_with_type.GetAddressType(), address_with_type.GetAddress(),
+                                                    0x01 /* 1M PHY ONLY */, {tmp}),
+          handler_->BindOnce([](CommandStatusView status) {
+            ASSERT(status.IsValid());
+            ASSERT(status.GetCommandOpCode() == OpCode::LE_EXTENDED_CREATE_CONNECTION);
+          }));
+    } else {
+      le_acl_connection_interface_->EnqueueCommand(
+          LeCreateConnectionBuilder::Create(le_scan_interval, le_scan_window, initiator_filter_policy,
+                                            address_with_type.GetAddressType(), address_with_type.GetAddress(),
+                                            own_address_type, conn_interval_min, conn_interval_max, conn_latency,
+                                            supervision_timeout, kMinimumCeLength, kMaximumCeLength),
+          handler_->BindOnce([](CommandStatusView status) {
+            ASSERT(status.IsValid());
+            ASSERT(status.GetCommandOpCode() == OpCode::LE_CREATE_CONNECTION);
+          }));
+    }
+  }
+
+  void cancel_connect(AddressWithType address_with_type) {
+    // the connection will be canceled by LeAddressManager.OnPause()
+    remove_device_from_connect_list(address_with_type);
+  }
+
+  void remove_device_from_connect_list(AddressWithType address_with_type) {
+    AddressType address_type = address_with_type.GetAddressType();
+    switch (address_type) {
+      case AddressType::PUBLIC_DEVICE_ADDRESS:
+      case AddressType::PUBLIC_IDENTITY_ADDRESS: {
+        le_address_manager_->RemoveDeviceFromConnectList(
+            ConnectListAddressType::PUBLIC, address_with_type.GetAddress());
+      } break;
+      case AddressType::RANDOM_DEVICE_ADDRESS:
+      case AddressType::RANDOM_IDENTITY_ADDRESS: {
+        le_address_manager_->RemoveDeviceFromConnectList(
+            ConnectListAddressType::RANDOM, address_with_type.GetAddress());
+      }
+    }
+  }
+
+  void set_privacy_policy_for_initiator_address(
+      LeAddressManager::AddressPolicy address_policy,
+      AddressWithType fixed_address,
+      crypto_toolbox::Octet16 rotation_irk,
+      std::chrono::milliseconds minimum_rotation_time,
+      std::chrono::milliseconds maximum_rotation_time) {
+    le_address_manager_->SetPrivacyPolicyForInitiatorAddress(
+        address_policy, fixed_address, rotation_irk, minimum_rotation_time, maximum_rotation_time);
+  }
+
+  void handle_register_le_callbacks(LeConnectionCallbacks* callbacks, os::Handler* handler) {
+    ASSERT(le_client_callbacks_ == nullptr);
+    ASSERT(le_client_handler_ == nullptr);
+    le_client_callbacks_ = callbacks;
+    le_client_handler_ = handler;
+  }
+
+  le_acl_connection& check_and_get_le_connection(uint16_t handle) {
+    auto connection = le_acl_connections_.find(handle);
+    ASSERT(connection != le_acl_connections_.end());
+    return connection->second;
+  }
+
+  void OnPause() override {
+    pause_connection = true;
+    if (connecting_le_.empty()) {
+      le_address_manager_->AckPause(this);
+      return;
+    }
+    canceled_connections_ = connecting_le_;
+    le_acl_connection_interface_->EnqueueCommand(
+        LeCreateConnectionCancelBuilder::Create(),
+        handler_->BindOnce(&le_impl::on_create_connection_cancel_complete, common::Unretained(this)));
+  }
+
+  void on_create_connection_cancel_complete(CommandCompleteView view) {
+    auto complete_view = LeCreateConnectionCancelCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ASSERT(complete_view.GetStatus() == ErrorCode::SUCCESS);
+    le_address_manager_->AckPause(this);
+  }
+
+  void check_for_unregister() {
+    if (le_acl_connections_.empty() && connecting_le_.empty() && canceled_connections_.empty() &&
+        address_manager_registered) {
+      le_address_manager_->Unregister(this);
+      address_manager_registered = false;
+      pause_connection = false;
+    }
+  }
+
+  void OnResume() override {
+    pause_connection = false;
+    if (!canceled_connections_.empty()) {
+      create_le_connection(*canceled_connections_.begin(), false);
+    }
+    canceled_connections_.clear();
+    le_address_manager_->AckResume(this);
+  }
+
+  uint16_t HACK_get_handle(Address address) {
+    for (auto it = le_acl_connections_.begin(); it != le_acl_connections_.end(); it++) {
+      if (it->second.address_with_type_.GetAddress() == address) {
+        return it->first;
+      }
+    }
+    return 0xFFFF;
+  }
+
+  static constexpr uint16_t kMinimumCeLength = 0x0002;
+  static constexpr uint16_t kMaximumCeLength = 0x0C00;
+  HciLayer* hci_layer_ = nullptr;
+  Controller* controller_ = nullptr;
+  os::Handler* handler_ = nullptr;
+  RoundRobinScheduler* round_robin_scheduler_ = nullptr;
+  LeAddressManager* le_address_manager_ = nullptr;
+  LeAclConnectionInterface* le_acl_connection_interface_ = nullptr;
+  LeConnectionCallbacks* le_client_callbacks_ = nullptr;
+  os::Handler* le_client_handler_ = nullptr;
+  std::map<uint16_t, le_acl_connection> le_acl_connections_;
+  std::set<AddressWithType> connecting_le_;
+  std::set<AddressWithType> canceled_connections_;
+  DisconnectorForLe* disconnector_;
+  bool address_manager_registered = false;
+  bool pause_connection = false;
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/round_robin_scheduler.cc b/gd/hci/acl_manager/round_robin_scheduler.cc
new file mode 100644
index 0000000..5bf54f5
--- /dev/null
+++ b/gd/hci/acl_manager/round_robin_scheduler.cc
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+
+#include "hci/acl_manager/round_robin_scheduler.h"
+#include "hci/acl_manager/acl_fragmenter.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+RoundRobinScheduler::RoundRobinScheduler(os::Handler* handler, Controller* controller,
+                                         common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* hci_queue_end)
+    : handler_(handler), controller_(controller), hci_queue_end_(hci_queue_end) {
+  max_acl_packet_credits_ = controller_->GetControllerNumAclPacketBuffers();
+  acl_packet_credits_ = max_acl_packet_credits_;
+  hci_mtu_ = controller_->GetControllerAclPacketLength();
+  LeBufferSize le_buffer_size = controller_->GetControllerLeBufferSize();
+  le_max_acl_packet_credits_ = le_buffer_size.total_num_le_packets_;
+  le_acl_packet_credits_ = le_max_acl_packet_credits_;
+  le_hci_mtu_ = le_buffer_size.le_data_packet_length_;
+  controller_->RegisterCompletedAclPacketsCallback(handler->BindOn(this, &RoundRobinScheduler::incoming_acl_credits));
+}
+
+RoundRobinScheduler::~RoundRobinScheduler() {
+  unregister_all_connections();
+  controller_->UnregisterCompletedAclPacketsCallback();
+}
+
+void RoundRobinScheduler::Register(ConnectionType connection_type, uint16_t handle,
+                                   std::shared_ptr<acl_manager::AclConnection::Queue> queue) {
+  acl_queue_handler acl_queue_handler = {connection_type, std::move(queue), false, 0};
+  acl_queue_handlers_.insert(std::pair<uint16_t, RoundRobinScheduler::acl_queue_handler>(handle, acl_queue_handler));
+  if (fragments_to_send_.size() == 0) {
+    start_round_robin();
+  }
+}
+
+void RoundRobinScheduler::Unregister(uint16_t handle) {
+  ASSERT(acl_queue_handlers_.count(handle) == 1);
+  auto acl_queue_handler = acl_queue_handlers_.find(handle)->second;
+  // Reclaim outstanding packets
+  if (acl_queue_handler.connection_type_ == ConnectionType::CLASSIC) {
+    acl_packet_credits_ += acl_queue_handler.number_of_sent_packets_;
+  } else {
+    le_acl_packet_credits_ += acl_queue_handler.number_of_sent_packets_;
+  }
+  acl_queue_handler.number_of_sent_packets_ = 0;
+
+  if (acl_queue_handler.dequeue_is_registered_) {
+    acl_queue_handler.dequeue_is_registered_ = false;
+    acl_queue_handler.queue_->GetDownEnd()->UnregisterDequeue();
+  }
+  acl_queue_handlers_.erase(handle);
+  starting_point_ = acl_queue_handlers_.begin();
+}
+
+uint16_t RoundRobinScheduler::GetCredits() {
+  return acl_packet_credits_;
+}
+
+uint16_t RoundRobinScheduler::GetLeCredits() {
+  return le_acl_packet_credits_;
+}
+
+void RoundRobinScheduler::start_round_robin() {
+  if (acl_packet_credits_ == 0 && le_acl_packet_credits_ == 0) {
+    return;
+  }
+  if (!fragments_to_send_.empty()) {
+    send_next_fragment();
+    return;
+  }
+
+  if (acl_queue_handlers_.size() == 1 || starting_point_ == acl_queue_handlers_.end()) {
+    starting_point_ = acl_queue_handlers_.begin();
+  }
+  size_t count = acl_queue_handlers_.size();
+
+  for (auto acl_queue_handler = starting_point_; count > 0; count--) {
+    // Prevent registration when credits is zero
+    bool classic_buffer_full =
+        acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::CLASSIC;
+    bool le_buffer_full =
+        le_acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::LE;
+    if (!acl_queue_handler->second.dequeue_is_registered_ && !classic_buffer_full && !le_buffer_full) {
+      acl_queue_handler->second.dequeue_is_registered_ = true;
+      acl_queue_handler->second.queue_->GetDownEnd()->RegisterDequeue(
+          handler_, common::Bind(&RoundRobinScheduler::buffer_packet, common::Unretained(this), acl_queue_handler));
+    }
+    acl_queue_handler = std::next(acl_queue_handler);
+    if (acl_queue_handler == acl_queue_handlers_.end()) {
+      acl_queue_handler = acl_queue_handlers_.begin();
+    }
+  }
+
+  starting_point_ = std::next(starting_point_);
+}
+
+void RoundRobinScheduler::buffer_packet(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler) {
+  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
+  // Wrap packet and enqueue it
+  uint16_t handle = acl_queue_handler->first;
+  auto packet = acl_queue_handler->second.queue_->GetDownEnd()->TryDequeue();
+  ASSERT(packet != nullptr);
+
+  ConnectionType connection_type = acl_queue_handler->second.connection_type_;
+  size_t mtu = connection_type == ConnectionType::CLASSIC ? hci_mtu_ : le_hci_mtu_;
+  PacketBoundaryFlag packet_boundary_flag =
+      (connection_type == ConnectionType::CLASSIC ? PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE
+                                                  : PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE);
+  if (packet->size() <= mtu) {
+    fragments_to_send_.push(std::make_pair(
+        connection_type, AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(packet))));
+  } else {
+    auto fragments = AclFragmenter(mtu, std::move(packet)).GetFragments();
+    for (size_t i = 0; i < fragments.size(); i++) {
+      fragments_to_send_.push(std::make_pair(
+          connection_type,
+          AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(fragments[i]))));
+      packet_boundary_flag = PacketBoundaryFlag::CONTINUING_FRAGMENT;
+    }
+  }
+  ASSERT(fragments_to_send_.size() > 0);
+  unregister_all_connections();
+
+  acl_queue_handler->second.number_of_sent_packets_ += fragments_to_send_.size();
+  send_next_fragment();
+}
+
+void RoundRobinScheduler::unregister_all_connections() {
+  for (auto acl_queue_handler = acl_queue_handlers_.begin(); acl_queue_handler != acl_queue_handlers_.end();
+       acl_queue_handler = std::next(acl_queue_handler)) {
+    if (acl_queue_handler->second.dequeue_is_registered_) {
+      acl_queue_handler->second.dequeue_is_registered_ = false;
+      acl_queue_handler->second.queue_->GetDownEnd()->UnregisterDequeue();
+    }
+  }
+}
+
+void RoundRobinScheduler::send_next_fragment() {
+  if (!enqueue_registered_.exchange(true)) {
+    hci_queue_end_->RegisterEnqueue(
+        handler_, common::Bind(&RoundRobinScheduler::handle_enqueue_next_fragment, common::Unretained(this)));
+  }
+}
+
+// Invoked from some external Queue Reactable context 1
+std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::handle_enqueue_next_fragment() {
+  ConnectionType connection_type = fragments_to_send_.front().first;
+  if (connection_type == ConnectionType::CLASSIC) {
+    ASSERT(acl_packet_credits_ > 0);
+    acl_packet_credits_ -= 1;
+  } else {
+    ASSERT(le_acl_packet_credits_ > 0);
+    le_acl_packet_credits_ -= 1;
+  }
+
+  auto raw_pointer = fragments_to_send_.front().second.release();
+  fragments_to_send_.pop();
+  if (fragments_to_send_.empty()) {
+    if (enqueue_registered_.exchange(false)) {
+      hci_queue_end_->UnregisterEnqueue();
+    }
+    handler_->Post(common::BindOnce(&RoundRobinScheduler::start_round_robin, common::Unretained(this)));
+  } else {
+    ConnectionType next_connection_type = fragments_to_send_.front().first;
+    bool classic_buffer_full = next_connection_type == ConnectionType::CLASSIC && acl_packet_credits_ == 0;
+    bool le_buffer_full = next_connection_type == ConnectionType::LE && le_acl_packet_credits_ == 0;
+    if ((classic_buffer_full || le_buffer_full) && enqueue_registered_.exchange(false)) {
+      hci_queue_end_->UnregisterEnqueue();
+    }
+  }
+  return std::unique_ptr<AclPacketBuilder>(raw_pointer);
+}
+
+void RoundRobinScheduler::incoming_acl_credits(uint16_t handle, uint16_t credits) {
+  auto acl_queue_handler = acl_queue_handlers_.find(handle);
+  if (acl_queue_handler == acl_queue_handlers_.end()) {
+    LOG_INFO("Dropping %hx received credits to unknown connection 0x%0hx", credits, handle);
+    return;
+  }
+  acl_queue_handler->second.number_of_sent_packets_ -= credits;
+  if (acl_queue_handler->second.connection_type_ == ConnectionType::CLASSIC) {
+    acl_packet_credits_ += credits;
+  } else {
+    le_acl_packet_credits_ += credits;
+  }
+  ASSERT(acl_packet_credits_ <= max_acl_packet_credits_);
+  ASSERT(le_acl_packet_credits_ <= le_max_acl_packet_credits_);
+  if (acl_packet_credits_ == credits || le_acl_packet_credits_ == credits) {
+    start_round_robin();
+  }
+}
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager/round_robin_scheduler.h b/gd/hci/acl_manager/round_robin_scheduler.h
new file mode 100644
index 0000000..997e6d6
--- /dev/null
+++ b/gd/hci/acl_manager/round_robin_scheduler.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "common/bidi_queue.h"
+#include "hci/acl_manager.h"
+#include "hci/controller.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class RoundRobinScheduler {
+ public:
+  RoundRobinScheduler(os::Handler* handler, Controller* controller,
+                      common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* hci_queue_end);
+  ~RoundRobinScheduler();
+
+  enum ConnectionType { CLASSIC, LE };
+
+  struct acl_queue_handler {
+    ConnectionType connection_type_;
+    std::shared_ptr<acl_manager::AclConnection::Queue> queue_;
+    bool dequeue_is_registered_ = false;
+    uint16_t number_of_sent_packets_ = 0;  // Track credits
+  };
+
+  void Register(ConnectionType connection_type, uint16_t handle,
+                std::shared_ptr<acl_manager::AclConnection::Queue> queue);
+  void Unregister(uint16_t handle);
+  uint16_t GetCredits();
+  uint16_t GetLeCredits();
+
+ private:
+  void start_round_robin();
+  void buffer_packet(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler);
+  void unregister_all_connections();
+  void send_next_fragment();
+  std::unique_ptr<AclPacketBuilder> handle_enqueue_next_fragment();
+  void incoming_acl_credits(uint16_t handle, uint16_t credits);
+
+  os::Handler* handler_ = nullptr;
+  Controller* controller_ = nullptr;
+  std::map<uint16_t, acl_queue_handler> acl_queue_handlers_;
+  std::queue<std::pair<ConnectionType, std::unique_ptr<AclPacketBuilder>>> fragments_to_send_;
+  uint16_t max_acl_packet_credits_ = 0;
+  uint16_t acl_packet_credits_ = 0;
+  uint16_t le_max_acl_packet_credits_ = 0;
+  uint16_t le_acl_packet_credits_ = 0;
+  size_t hci_mtu_{0};
+  size_t le_hci_mtu_{0};
+  std::atomic_bool enqueue_registered_ = false;
+  common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* hci_queue_end_ = nullptr;
+  // first register queue end for the Round-robin schedule
+  std::map<uint16_t, acl_queue_handler>::iterator starting_point_;
+};
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/hci/acl_manager/round_robin_scheduler_test.cc b/gd/hci/acl_manager/round_robin_scheduler_test.cc
new file mode 100644
index 0000000..4e761ca
--- /dev/null
+++ b/gd/hci/acl_manager/round_robin_scheduler_test.cc
@@ -0,0 +1,375 @@
+/*
+ * 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.
+ */
+
+#include "hci/acl_manager/round_robin_scheduler.h"
+
+#include <gtest/gtest.h>
+
+#include "common/bidi_queue.h"
+#include "common/callback.h"
+#include "hci/acl_manager.h"
+#include "hci/controller.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+#include "os/log.h"
+#include "packet/raw_builder.h"
+
+using ::bluetooth::common::BidiQueue;
+using ::bluetooth::common::Callback;
+using ::bluetooth::os::Handler;
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+namespace hci {
+namespace acl_manager {
+
+class TestController : public Controller {
+ public:
+  uint16_t GetControllerNumAclPacketBuffers() const {
+    return max_acl_packet_credits_;
+  }
+
+  uint16_t GetControllerAclPacketLength() const {
+    return hci_mtu_;
+  }
+
+  LeBufferSize GetControllerLeBufferSize() const {
+    LeBufferSize le_buffer_size;
+    le_buffer_size.le_data_packet_length_ = le_hci_mtu_;
+    le_buffer_size.total_num_le_packets_ = le_max_acl_packet_credits_;
+    return le_buffer_size;
+  }
+
+  void RegisterCompletedAclPacketsCallback(CompletedAclPacketsCallback cb) {
+    acl_credits_callback_ = cb;
+  }
+
+  void SendCompletedAclPacketsCallback(uint16_t handle, uint16_t credits) {
+    acl_credits_callback_.Invoke(handle, credits);
+  }
+
+  void UnregisterCompletedAclPacketsCallback() {
+    acl_credits_callback_ = {};
+  }
+
+  const uint16_t max_acl_packet_credits_ = 10;
+  const uint16_t hci_mtu_ = 1024;
+  const uint16_t le_max_acl_packet_credits_ = 15;
+  const uint16_t le_hci_mtu_ = 27;
+
+ private:
+  CompletedAclPacketsCallback acl_credits_callback_;
+};
+
+class RoundRobinSchedulerTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    thread_ = new Thread("thread", Thread::Priority::NORMAL);
+    handler_ = new Handler(thread_);
+    controller_ = new TestController();
+    round_robin_scheduler_ = new RoundRobinScheduler(handler_, controller_, hci_queue_.GetUpEnd());
+    hci_queue_.GetDownEnd()->RegisterDequeue(
+        handler_, common::Bind(&RoundRobinSchedulerTest::HciDownEndDequeue, common::Unretained(this)));
+  }
+
+  void TearDown() override {
+    hci_queue_.GetDownEnd()->UnregisterDequeue();
+    delete round_robin_scheduler_;
+    delete controller_;
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+
+  void sync_handler() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler_->BindOnceOn(&promise, &std::promise<void>::set_value).Invoke();
+    auto status = future.wait_for(std::chrono::milliseconds(3));
+    EXPECT_EQ(status, std::future_status::ready);
+  }
+
+  void EnqueueAclUpEnd(AclConnection::QueueUpEnd* queue_up_end, std::vector<uint8_t> packet) {
+    if (enqueue_promise_ != nullptr) {
+      enqueue_future_->wait();
+    }
+    enqueue_promise_ = std::make_unique<std::promise<void>>();
+    enqueue_future_ = std::make_unique<std::future<void>>(enqueue_promise_->get_future());
+    queue_up_end->RegisterEnqueue(handler_, common::Bind(&RoundRobinSchedulerTest::enqueue_callback,
+                                                         common::Unretained(this), queue_up_end, packet));
+  }
+
+  std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(AclConnection::QueueUpEnd* queue_up_end,
+                                                              std::vector<uint8_t> packet) {
+    auto packet_one = std::make_unique<packet::RawBuilder>(2000);
+    packet_one->AddOctets(packet);
+    queue_up_end->UnregisterEnqueue();
+    enqueue_promise_->set_value();
+    return packet_one;
+  };
+
+  void HciDownEndDequeue() {
+    auto packet = hci_queue_.GetDownEnd()->TryDequeue();
+    // Convert from a Builder to a View
+    auto bytes = std::make_shared<std::vector<uint8_t>>();
+    bluetooth::packet::BitInserter i(*bytes);
+    bytes->reserve(packet->size());
+    packet->Serialize(i);
+    auto packet_view = bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian>(bytes);
+    AclPacketView acl_packet_view = AclPacketView::Create(packet_view);
+    ASSERT_TRUE(acl_packet_view.IsValid());
+    PacketView<true> count_view = acl_packet_view.GetPayload();
+    sent_acl_packets_.push(acl_packet_view);
+
+    packet_count_--;
+    if (packet_count_ == 0) {
+      packet_promise_->set_value();
+      packet_promise_ = nullptr;
+    }
+  }
+
+  void VerifyPacket(uint16_t handle, std::vector<uint8_t> packet) {
+    auto acl_packet_view = sent_acl_packets_.front();
+    ASSERT_EQ(handle, acl_packet_view.GetHandle());
+    auto payload = acl_packet_view.GetPayload();
+    for (size_t i = 0; i < payload.size(); i++) {
+      ASSERT_EQ(payload[i], packet[i]);
+    }
+    sent_acl_packets_.pop();
+  }
+
+  void SetPacketFuture(uint16_t count) {
+    ASSERT_LOG(packet_promise_ == nullptr, "Promises, Promises, ... Only one at a time.");
+    packet_count_ = count;
+    packet_promise_ = std::make_unique<std::promise<void>>();
+    packet_future_ = std::make_unique<std::future<void>>(packet_promise_->get_future());
+  }
+
+  BidiQueue<AclPacketView, AclPacketBuilder> hci_queue_{3};
+  Thread* thread_;
+  Handler* handler_;
+  TestController* controller_;
+  RoundRobinScheduler* round_robin_scheduler_;
+  std::queue<AclPacketView> sent_acl_packets_;
+  uint16_t packet_count_;
+  std::unique_ptr<std::promise<void>> packet_promise_;
+  std::unique_ptr<std::future<void>> packet_future_;
+  std::unique_ptr<std::promise<void>> enqueue_promise_;
+  std::unique_ptr<std::future<void>> enqueue_future_;
+};
+
+TEST_F(RoundRobinSchedulerTest, startup_teardown) {}
+
+TEST_F(RoundRobinSchedulerTest, register_unregister_connection) {
+  uint16_t handle = 0x01;
+  auto connection_queue = std::make_shared<AclConnection::Queue>(10);
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue);
+  round_robin_scheduler_->Unregister(handle);
+}
+
+TEST_F(RoundRobinSchedulerTest, buffer_packet) {
+  uint16_t handle = 0x01;
+  auto connection_queue = std::make_shared<AclConnection::Queue>(10);
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue);
+
+  SetPacketFuture(2);
+  AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd();
+  std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03};
+  std::vector<uint8_t> packet2 = {0x04, 0x05, 0x06};
+  EnqueueAclUpEnd(queue_up_end, packet1);
+  EnqueueAclUpEnd(queue_up_end, packet2);
+
+  packet_future_->wait();
+  VerifyPacket(handle, packet1);
+  VerifyPacket(handle, packet2);
+  ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2);
+
+  round_robin_scheduler_->Unregister(handle);
+}
+
+TEST_F(RoundRobinSchedulerTest, buffer_packet_from_two_connections) {
+  uint16_t handle = 0x01;
+  uint16_t le_handle = 0x02;
+  auto connection_queue = std::make_shared<AclConnection::Queue>(10);
+  auto le_connection_queue = std::make_shared<AclConnection::Queue>(10);
+
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue);
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_connection_queue);
+
+  SetPacketFuture(2);
+  AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd();
+  AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue->GetUpEnd();
+  std::vector<uint8_t> packet = {0x01, 0x02, 0x03};
+  std::vector<uint8_t> le_packet = {0x04, 0x05, 0x06};
+  EnqueueAclUpEnd(le_queue_up_end, le_packet);
+  EnqueueAclUpEnd(queue_up_end, packet);
+
+  packet_future_->wait();
+  VerifyPacket(le_handle, le_packet);
+  VerifyPacket(handle, packet);
+  ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 1);
+  ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 1);
+
+  round_robin_scheduler_->Unregister(handle);
+  round_robin_scheduler_->Unregister(le_handle);
+}
+
+TEST_F(RoundRobinSchedulerTest, do_not_register_when_credits_is_zero) {
+  uint16_t handle = 0x01;
+  auto connection_queue = std::make_shared<AclConnection::Queue>(15);
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue);
+
+  SetPacketFuture(10);
+  AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd();
+  for (uint8_t i = 0; i < 15; i++) {
+    std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i};
+    EnqueueAclUpEnd(queue_up_end, packet);
+  }
+
+  packet_future_->wait();
+  for (uint8_t i = 0; i < 10; i++) {
+    std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i};
+    VerifyPacket(handle, packet);
+  }
+  ASSERT_EQ(round_robin_scheduler_->GetCredits(), 0);
+
+  SetPacketFuture(5);
+  controller_->SendCompletedAclPacketsCallback(0x01, 10);
+  sync_handler();
+  packet_future_->wait();
+  for (uint8_t i = 10; i < 15; i++) {
+    std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i};
+    VerifyPacket(handle, packet);
+  }
+  ASSERT_EQ(round_robin_scheduler_->GetCredits(), 5);
+
+  round_robin_scheduler_->Unregister(handle);
+}
+
+TEST_F(RoundRobinSchedulerTest, reveived_completed_callback_with_unknown_handle) {
+  controller_->SendCompletedAclPacketsCallback(0x00, 1);
+  sync_handler();
+  EXPECT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_);
+  EXPECT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_);
+}
+
+TEST_F(RoundRobinSchedulerTest, buffer_packet_intervally) {
+  uint16_t handle1 = 0x01;
+  uint16_t handle2 = 0x02;
+  uint16_t le_handle1 = 0x03;
+  uint16_t le_handle2 = 0x04;
+  auto connection_queue1 = std::make_shared<AclConnection::Queue>(10);
+  auto connection_queue2 = std::make_shared<AclConnection::Queue>(10);
+  auto le_connection_queue1 = std::make_shared<AclConnection::Queue>(10);
+  auto le_connection_queue2 = std::make_shared<AclConnection::Queue>(10);
+
+  SetPacketFuture(18);
+  AclConnection::QueueUpEnd* queue_up_end1 = connection_queue1->GetUpEnd();
+  AclConnection::QueueUpEnd* queue_up_end2 = connection_queue2->GetUpEnd();
+  AclConnection::QueueUpEnd* le_queue_up_end1 = le_connection_queue1->GetUpEnd();
+  AclConnection::QueueUpEnd* le_queue_up_end2 = le_connection_queue2->GetUpEnd();
+
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle1, connection_queue1);
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle2, connection_queue2);
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle1, le_connection_queue1);
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle2, le_connection_queue2);
+
+  std::vector<uint8_t> packet = {0x01, 0x02, 0x03};
+  EnqueueAclUpEnd(queue_up_end1, packet);
+  EnqueueAclUpEnd(le_queue_up_end2, packet);
+  for (uint8_t i = 0; i < 4; i++) {
+    std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03, i};
+    std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i};
+    std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i};
+    std::vector<uint8_t> le_packet2 = {0x05, 0x05, 0x06, i};
+    EnqueueAclUpEnd(queue_up_end1, packet1);
+    EnqueueAclUpEnd(queue_up_end2, packet2);
+    EnqueueAclUpEnd(le_queue_up_end1, le_packet1);
+    EnqueueAclUpEnd(le_queue_up_end2, le_packet2);
+  }
+
+  packet_future_->wait();
+  VerifyPacket(handle1, packet);
+  VerifyPacket(le_handle2, packet);
+  for (uint8_t i = 0; i < 4; i++) {
+    std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03, i};
+    std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i};
+    std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i};
+    std::vector<uint8_t> le_packet2 = {0x05, 0x05, 0x06, i};
+    VerifyPacket(handle1, packet1);
+    VerifyPacket(handle2, packet2);
+    VerifyPacket(le_handle1, le_packet1);
+    VerifyPacket(le_handle2, le_packet2);
+  }
+
+  ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 9);
+  ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 9);
+
+  round_robin_scheduler_->Unregister(handle1);
+  round_robin_scheduler_->Unregister(handle2);
+  round_robin_scheduler_->Unregister(le_handle1);
+  round_robin_scheduler_->Unregister(le_handle2);
+}
+
+TEST_F(RoundRobinSchedulerTest, send_fragments_without_interval) {
+  uint16_t handle = 0x01;
+  uint16_t le_handle = 0x02;
+  auto connection_queue = std::make_shared<AclConnection::Queue>(10);
+  auto le_connection_queue = std::make_shared<AclConnection::Queue>(10);
+
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue);
+  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_connection_queue);
+
+  SetPacketFuture(5);
+  AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd();
+  AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue->GetUpEnd();
+  std::vector<uint8_t> packet(controller_->hci_mtu_, 0xff);
+  std::vector<uint8_t> packet_part1(controller_->hci_mtu_, 0xff);
+  std::vector<uint8_t> packet_part2 = {0x03, 0x02, 0x01};
+  packet.insert(packet.end(), packet_part2.begin(), packet_part2.end());
+
+  std::vector<uint8_t> le_packet;
+  std::vector<uint8_t> le_packet_part1;
+  std::vector<uint8_t> le_packet_part2;
+  std::vector<uint8_t> le_packet_part3;
+  for (uint8_t i = 0; i < controller_->le_hci_mtu_; i++) {
+    le_packet.push_back(i);
+    le_packet_part1.push_back(i);
+    le_packet_part2.push_back(i * 2);
+    le_packet_part3.push_back(i * 3);
+  }
+  le_packet.insert(le_packet.end(), le_packet_part2.begin(), le_packet_part2.end());
+  le_packet.insert(le_packet.end(), le_packet_part3.begin(), le_packet_part3.end());
+
+  EnqueueAclUpEnd(le_queue_up_end, le_packet);
+  EnqueueAclUpEnd(queue_up_end, packet);
+
+  packet_future_->wait();
+  VerifyPacket(le_handle, le_packet_part1);
+  VerifyPacket(le_handle, le_packet_part2);
+  VerifyPacket(le_handle, le_packet_part3);
+  VerifyPacket(handle, packet_part1);
+  VerifyPacket(handle, packet_part2);
+  ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2);
+  ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 3);
+
+  round_robin_scheduler_->Unregister(handle);
+  round_robin_scheduler_->Unregister(le_handle);
+}
+
+}  // namespace acl_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager_mock.h b/gd/hci/acl_manager_mock.h
index e7a8c6b..04d8e8b 100644
--- a/gd/hci/acl_manager_mock.h
+++ b/gd/hci/acl_manager_mock.h
@@ -16,6 +16,12 @@
 #pragma once
 
 #include "hci/acl_manager.h"
+#include "hci/acl_manager/classic_acl_connection.h"
+#include "hci/acl_manager/connection_callbacks.h"
+#include "hci/acl_manager/connection_management_callbacks.h"
+#include "hci/acl_manager/le_acl_connection.h"
+#include "hci/acl_manager/le_connection_callbacks.h"
+#include "hci/acl_manager/le_connection_management_callbacks.h"
 
 #include <gmock/gmock.h>
 
@@ -24,14 +30,33 @@
 namespace hci {
 namespace testing {
 
-class MockAclConnection : public AclConnection {
+using acl_manager::LeAclConnection;
+using acl_manager::LeConnectionCallbacks;
+using acl_manager::LeConnectionManagementCallbacks;
+
+using acl_manager::ClassicAclConnection;
+using acl_manager::ConnectionCallbacks;
+using acl_manager::ConnectionManagementCallbacks;
+
+class MockClassicAclConnection : public ClassicAclConnection {
  public:
   MOCK_METHOD(Address, GetAddress, (), (const, override));
-  MOCK_METHOD(AddressType, GetAddressType, (), (const, override));
-  MOCK_METHOD(void, RegisterDisconnectCallback,
-              (common::OnceCallback<void(ErrorCode)> on_disconnect, os::Handler* handler), (override));
   MOCK_METHOD(bool, Disconnect, (DisconnectReason reason), (override));
-  MOCK_METHOD(void, Finish, (), (override));
+  MOCK_METHOD(void, RegisterCallbacks, (ConnectionManagementCallbacks * callbacks, os::Handler* handler), (override));
+
+  QueueUpEnd* GetAclQueueEnd() const override {
+    return acl_queue_.GetUpEnd();
+  }
+  mutable common::BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> acl_queue_{10};
+};
+
+class MockLeAclConnection : public LeAclConnection {
+ public:
+  MOCK_METHOD(AddressWithType, GetLocalAddress, (), (const, override));
+  MOCK_METHOD(AddressWithType, GetRemoteAddress, (), (const, override));
+  MOCK_METHOD(void, Disconnect, (DisconnectReason reason), (override));
+  MOCK_METHOD(void, RegisterCallbacks, (LeConnectionManagementCallbacks * callbacks, os::Handler* handler), (override));
+
   QueueUpEnd* GetAclQueueEnd() const override {
     return acl_queue_.GetUpEnd();
   }
@@ -45,8 +70,17 @@
   MOCK_METHOD(void, CreateConnection, (Address address), (override));
   MOCK_METHOD(void, CreateLeConnection, (AddressWithType address_with_type), (override));
   MOCK_METHOD(void, CancelConnect, (Address address), (override));
+  MOCK_METHOD(
+      void,
+      SetPrivacyPolicyForInitiatorAddress,
+      (LeAddressManager::AddressPolicy address_policy,
+       AddressWithType fixed_address,
+       crypto_toolbox::Octet16 rotation_irk,
+       std::chrono::milliseconds minimum_rotation_time,
+       std::chrono::milliseconds maximum_rotation_time),
+      (override));
 };
 
 }  // namespace testing
 }  // namespace hci
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager_test.cc b/gd/hci/acl_manager_test.cc
index 50c4e05..de25392 100644
--- a/gd/hci/acl_manager_test.cc
+++ b/gd/hci/acl_manager_test.cc
@@ -33,6 +33,7 @@
 
 namespace bluetooth {
 namespace hci {
+namespace acl_manager {
 namespace {
 
 using common::BidiQueue;
@@ -54,6 +55,8 @@
 std::unique_ptr<BasePacketBuilder> NextPayload(uint16_t handle) {
   static uint32_t packet_number = 1;
   auto payload = std::make_unique<RawBuilder>();
+  payload->AddOctets2(6);  // L2CAP PDU size
+  payload->AddOctets2(2);  // L2CAP CID
   payload->AddOctets2(handle);
   payload->AddOctets4(packet_number++);
   return std::move(payload);
@@ -67,10 +70,13 @@
 
 class TestController : public Controller {
  public:
-  void RegisterCompletedAclPacketsCallback(common::Callback<void(uint16_t /* handle */, uint16_t /* packets */)> cb,
-                                           os::Handler* handler) override {
+  void RegisterCompletedAclPacketsCallback(
+      common::ContextualCallback<void(uint16_t /* handle */, uint16_t /* packets */)> cb) override {
     acl_cb_ = cb;
-    acl_cb_handler_ = handler;
+  }
+
+  void UnregisterCompletedAclPacketsCallback() override {
+    acl_cb_ = {};
   }
 
   uint16_t GetControllerAclPacketLength() const override {
@@ -81,14 +87,25 @@
     return total_acl_buffers_;
   }
 
+  LeBufferSize GetControllerLeBufferSize() const override {
+    LeBufferSize le_buffer_size;
+    le_buffer_size.total_num_le_packets_ = 2;
+    le_buffer_size.le_data_packet_length_ = 32;
+    return le_buffer_size;
+  }
+
+  uint64_t GetControllerLeLocalSupportedFeatures() const override {
+    return le_local_supported_features_;
+  }
+
   void CompletePackets(uint16_t handle, uint16_t packets) {
-    acl_cb_handler_->Post(common::BindOnce(acl_cb_, handle, packets));
+    acl_cb_.Invoke(handle, packets);
   }
 
   uint16_t acl_buffer_length_ = 1024;
   uint16_t total_acl_buffers_ = 2;
-  common::Callback<void(uint16_t /* handle */, uint16_t /* packets */)> acl_cb_;
-  os::Handler* acl_cb_handler_ = nullptr;
+  uint64_t le_local_supported_features_ = 0;
+  common::ContextualCallback<void(uint16_t /* handle */, uint16_t /* packets */)> acl_cb_;
 
  protected:
   void Start() override {}
@@ -99,7 +116,7 @@
 class TestHciLayer : public HciLayer {
  public:
   void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+                      common::ContextualOnceCallback<void(CommandStatusView)> on_status) override {
     command_queue_.push(std::move(command));
     command_status_callbacks.push_front(std::move(on_status));
     if (command_promise_ != nullptr) {
@@ -109,7 +126,7 @@
   }
 
   void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+                      common::ContextualOnceCallback<void(CommandCompleteView)> on_complete) override {
     command_queue_.push(std::move(command));
     command_complete_callbacks.push_front(std::move(on_complete));
     if (command_promise_ != nullptr) {
@@ -124,13 +141,13 @@
     command_future_ = std::make_unique<std::future<void>>(command_promise_->get_future());
   }
 
-  std::unique_ptr<CommandPacketBuilder> GetLastCommand() {
+  CommandPacketView GetLastCommand() {
     if (command_queue_.size() == 0) {
-      return nullptr;
+      return CommandPacketView::Create(std::make_shared<std::vector<uint8_t>>());
     }
     auto last = std::move(command_queue_.front());
     command_queue_.pop();
-    return last;
+    return CommandPacketView::Create(GetPacketView(std::move(last)));
   }
 
   ConnectionManagementCommandView GetCommandPacket(OpCode op_code) {
@@ -138,18 +155,20 @@
       auto result = command_future_->wait_for(std::chrono::milliseconds(1000));
       EXPECT_NE(std::future_status::timeout, result);
     }
-    ASSERT(command_queue_.size() > 0);
-    auto packet_view = GetPacketView(GetLastCommand());
-    CommandPacketView command_packet_view = CommandPacketView::Create(packet_view);
+    if (command_queue_.empty()) {
+      return ConnectionManagementCommandView::Create(
+          CommandPacketView::Create(std::make_shared<std::vector<uint8_t>>()));
+    }
+    CommandPacketView command_packet_view = GetLastCommand();
     ConnectionManagementCommandView command = ConnectionManagementCommandView::Create(command_packet_view);
-    ASSERT(command.IsValid());
+    EXPECT_TRUE(command.IsValid());
     EXPECT_EQ(command.GetOpCode(), op_code);
 
     return command;
   }
 
-  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
-                            os::Handler* handler) override {
+  void RegisterEventHandler(EventCode event_code,
+                            common::ContextualCallback<void(EventPacketView)> event_handler) override {
     registered_events_[event_code] = event_handler;
   }
 
@@ -157,12 +176,12 @@
     registered_events_.erase(event_code);
   }
 
-  void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
-                              os::Handler* handler) override {
+  void RegisterLeEventHandler(SubeventCode subevent_code,
+                              common::ContextualCallback<void(LeMetaEventView)> event_handler) override {
     registered_le_events_[subevent_code] = event_handler;
   }
 
-  void UnregisterLeEventHandler(SubeventCode subevent_code) {
+  void UnregisterLeEventHandler(SubeventCode subevent_code) override {
     registered_le_events_.erase(subevent_code);
   }
 
@@ -171,8 +190,8 @@
     EventPacketView event = EventPacketView::Create(packet);
     ASSERT_TRUE(event.IsValid());
     EventCode event_code = event.GetEventCode();
-    ASSERT_TRUE(registered_events_.find(event_code) != registered_events_.end()) << EventCodeText(event_code);
-    registered_events_[event_code].Run(event);
+    ASSERT_NE(registered_events_.find(event_code), registered_events_.end()) << EventCodeText(event_code);
+    registered_events_[event_code].Invoke(event);
   }
 
   void IncomingLeMetaEvent(std::unique_ptr<LeMetaEventBuilder> event_builder) {
@@ -182,7 +201,7 @@
     EXPECT_TRUE(meta_event_view.IsValid());
     SubeventCode subevent_code = meta_event_view.GetSubeventCode();
     EXPECT_TRUE(registered_le_events_.find(subevent_code) != registered_le_events_.end());
-    registered_le_events_[subevent_code].Run(meta_event_view);
+    registered_le_events_[subevent_code].Invoke(meta_event_view);
   }
 
   void IncomingAclData(uint16_t handle) {
@@ -211,15 +230,15 @@
 
   void CommandCompleteCallback(EventPacketView event) {
     CommandCompleteView complete_view = CommandCompleteView::Create(event);
-    ASSERT(complete_view.IsValid());
-    std::move(command_complete_callbacks.front()).Run(complete_view);
+    ASSERT_TRUE(complete_view.IsValid());
+    std::move(command_complete_callbacks.front()).Invoke(complete_view);
     command_complete_callbacks.pop_front();
   }
 
   void CommandStatusCallback(EventPacketView event) {
     CommandStatusView status_view = CommandStatusView::Create(event);
-    ASSERT(status_view.IsValid());
-    std::move(command_status_callbacks.front()).Run(status_view);
+    ASSERT_TRUE(status_view.IsValid());
+    std::move(command_status_callbacks.front()).Invoke(status_view);
     command_status_callbacks.pop_front();
   }
 
@@ -240,37 +259,65 @@
   void ListDependencies(ModuleList* list) override {}
   void Start() override {
     RegisterEventHandler(EventCode::COMMAND_COMPLETE,
-                         base::Bind(&TestHciLayer::CommandCompleteCallback, common::Unretained(this)), nullptr);
-    RegisterEventHandler(EventCode::COMMAND_STATUS,
-                         base::Bind(&TestHciLayer::CommandStatusCallback, common::Unretained(this)), nullptr);
+                         GetHandler()->BindOn(this, &TestHciLayer::CommandCompleteCallback));
+    RegisterEventHandler(EventCode::COMMAND_STATUS, GetHandler()->BindOn(this, &TestHciLayer::CommandStatusCallback));
   }
   void Stop() override {}
 
+  void Disconnect(uint16_t handle, ErrorCode reason) override {
+    GetHandler()->Post(common::BindOnce(&TestHciLayer::do_disconnect, common::Unretained(this), handle, reason));
+  }
+
  private:
-  std::map<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
-  std::map<SubeventCode, common::Callback<void(LeMetaEventView)>> registered_le_events_;
-  std::list<base::OnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
-  std::list<base::OnceCallback<void(CommandStatusView)>> command_status_callbacks;
+  std::map<EventCode, common::ContextualCallback<void(EventPacketView)>> registered_events_;
+  std::map<SubeventCode, common::ContextualCallback<void(LeMetaEventView)>> registered_le_events_;
+  std::list<common::ContextualOnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
+  std::list<common::ContextualOnceCallback<void(CommandStatusView)>> command_status_callbacks;
   BidiQueue<AclPacketView, AclPacketBuilder> acl_queue_{3 /* TODO: Set queue depth */};
 
   std::queue<std::unique_ptr<CommandPacketBuilder>> command_queue_;
   std::unique_ptr<std::promise<void>> command_promise_;
   std::unique_ptr<std::future<void>> command_future_;
+
+  void do_disconnect(uint16_t handle, ErrorCode reason) {
+    HciLayer::Disconnect(handle, reason);
+  }
 };
 
 class AclManagerNoCallbacksTest : public ::testing::Test {
  protected:
   void SetUp() override {
     test_hci_layer_ = new TestHciLayer;  // Ownership is transferred to registry
-    test_hci_layer_->Start();
     test_controller_ = new TestController;
     fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
     fake_registry_.InjectTestModule(&Controller::Factory, test_controller_);
     client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory);
-    EXPECT_NE(client_handler_, nullptr);
+    ASSERT_NE(client_handler_, nullptr);
+    test_hci_layer_->SetCommandFuture();
     fake_registry_.Start<AclManager>(&thread_);
     acl_manager_ = static_cast<AclManager*>(fake_registry_.GetModuleUnderTest(&AclManager::Factory));
     Address::FromString("A1:A2:A3:A4:A5:A6", remote);
+
+    // Verify LE Set Random Address was sent during setup
+    hci::Address address;
+    Address::FromString("D0:05:04:03:02:01", address);
+    hci::AddressWithType address_with_type(address, hci::AddressType::RANDOM_DEVICE_ADDRESS);
+    crypto_toolbox::Octet16 irk = {};
+    auto minimum_rotation_time = std::chrono::milliseconds(7 * 60 * 1000);
+    auto maximum_rotation_time = std::chrono::milliseconds(15 * 60 * 1000);
+    acl_manager_->SetPrivacyPolicyForInitiatorAddress(
+        LeAddressManager::AddressPolicy::USE_STATIC_ADDRESS,
+        address_with_type,
+        irk,
+        minimum_rotation_time,
+        maximum_rotation_time);
+
+    auto set_random_address_packet = LeSetRandomAddressView::Create(
+        LeAdvertisingCommandView::Create(test_hci_layer_->GetCommandPacket(OpCode::LE_SET_RANDOM_ADDRESS)));
+    ASSERT_TRUE(set_random_address_packet.IsValid());
+    my_initiating_address =
+        AddressWithType(set_random_address_packet.GetRandomAddress(), AddressType::RANDOM_DEVICE_ADDRESS);
+    test_hci_layer_->IncomingEvent(LeSetRandomAddressCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
   }
 
   void TearDown() override {
@@ -285,6 +332,7 @@
   AclManager* acl_manager_ = nullptr;
   os::Handler* client_handler_ = nullptr;
   Address remote;
+  AddressWithType my_initiating_address;
 
   std::future<void> GetConnectionFuture() {
     ASSERT_LOG(mock_connection_callback_.connection_promise_ == nullptr, "Promises promises ... Only one at a time");
@@ -299,16 +347,15 @@
     return mock_le_connection_callbacks_.le_connection_promise_->get_future();
   }
 
-  std::shared_ptr<AclConnection> GetLastConnection() {
+  std::shared_ptr<ClassicAclConnection> GetLastConnection() {
     return mock_connection_callback_.connections_.back();
   }
 
-  std::shared_ptr<AclConnection> GetLastLeConnection() {
+  std::shared_ptr<LeAclConnection> GetLastLeConnection() {
     return mock_le_connection_callbacks_.le_connections_.back();
   }
 
-  void SendAclData(uint16_t handle, std::shared_ptr<AclConnection> connection) {
-    auto queue_end = connection->GetAclQueueEnd();
+  void SendAclData(uint16_t handle, AclConnection::QueueUpEnd* queue_end) {
     std::promise<void> promise;
     auto future = promise.get_future();
     queue_end->RegisterEnqueue(client_handler_,
@@ -325,7 +372,7 @@
 
   class MockConnectionCallback : public ConnectionCallbacks {
    public:
-    void OnConnectSuccess(std::unique_ptr<AclConnection> connection) override {
+    void OnConnectSuccess(std::unique_ptr<ClassicAclConnection> connection) override {
       // Convert to std::shared_ptr during push_back()
       connections_.push_back(std::move(connection));
       if (connection_promise_ != nullptr) {
@@ -335,13 +382,13 @@
     }
     MOCK_METHOD(void, OnConnectFail, (Address, ErrorCode reason), (override));
 
-    std::list<std::shared_ptr<AclConnection>> connections_;
+    std::list<std::shared_ptr<ClassicAclConnection>> connections_;
     std::unique_ptr<std::promise<void>> connection_promise_;
   } mock_connection_callback_;
 
   class MockLeConnectionCallbacks : public LeConnectionCallbacks {
    public:
-    void OnLeConnectSuccess(AddressWithType address_with_type, std::unique_ptr<AclConnection> connection) override {
+    void OnLeConnectSuccess(AddressWithType address_with_type, std::unique_ptr<LeAclConnection> connection) override {
       le_connections_.push_back(std::move(connection));
       if (le_connection_promise_ != nullptr) {
         le_connection_promise_->set_value();
@@ -350,16 +397,15 @@
     }
     MOCK_METHOD(void, OnLeConnectFail, (AddressWithType, ErrorCode reason), (override));
 
-    std::list<std::shared_ptr<AclConnection>> le_connections_;
+    std::list<std::shared_ptr<LeAclConnection>> le_connections_;
     std::unique_ptr<std::promise<void>> le_connection_promise_;
   } mock_le_connection_callbacks_;
 
-  class MockAclManagerCallbacks : public AclManagerCallbacks {
+  class MockLeConnectionManagementCallbacks : public LeConnectionManagementCallbacks {
    public:
-    MOCK_METHOD(void, OnMasterLinkKeyComplete, (uint16_t connection_handle, KeyFlag key_flag), (override));
-    MOCK_METHOD(void, OnRoleChange, (Address bd_addr, Role new_role), (override));
-    MOCK_METHOD(void, OnReadDefaultLinkPolicySettingsComplete, (uint16_t default_link_policy_settings), (override));
-  } mock_acl_manager_callbacks_;
+    MOCK_METHOD(void, OnConnectionUpdate, (uint16_t, uint16_t, uint16_t), (override));
+    MOCK_METHOD(void, OnDisconnection, (ErrorCode), (override));
+  };
 };
 
 class AclManagerTest : public AclManagerNoCallbacksTest {
@@ -368,7 +414,6 @@
     AclManagerNoCallbacksTest::SetUp();
     acl_manager_->RegisterCallbacks(&mock_connection_callback_, client_handler_);
     acl_manager_->RegisterLeCallbacks(&mock_le_connection_callbacks_, client_handler_);
-    acl_manager_->RegisterAclManagerCallbacks(&mock_acl_manager_callbacks_, client_handler_);
   }
 };
 
@@ -381,10 +426,10 @@
     acl_manager_->CreateConnection(remote);
 
     // Wait for the connection request
-    std::unique_ptr<CommandPacketBuilder> last_command;
-    do {
-      last_command = test_hci_layer_->GetLastCommand();
-    } while (last_command == nullptr);
+    auto last_command = test_hci_layer_->GetCommandPacket(OpCode::CREATE_CONNECTION);
+    while (!last_command.IsValid()) {
+      last_command = test_hci_layer_->GetCommandPacket(OpCode::CREATE_CONNECTION);
+    }
 
     auto first_connection = GetConnectionFuture();
     test_hci_layer_->IncomingEvent(
@@ -397,8 +442,22 @@
     connection_->RegisterCallbacks(&mock_connection_management_callbacks_, client_handler_);
   }
 
+  void TearDown() override {
+    fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
+    fake_registry_.SynchronizeModuleHandler(&AclManager::Factory, std::chrono::milliseconds(20));
+    fake_registry_.StopAll();
+  }
+
+  void sync_client_handler() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    client_handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    auto future_status = future.wait_for(std::chrono::seconds(1));
+    EXPECT_EQ(future_status, std::future_status::ready);
+  }
+
   uint16_t handle_;
-  std::shared_ptr<AclConnection> connection_;
+  std::shared_ptr<ClassicAclConnection> connection_;
 
   class MockConnectionManagementCallbacks : public ConnectionManagementCallbacks {
    public:
@@ -424,6 +483,9 @@
     MOCK_METHOD2(OnReadAfhChannelMapComplete, void(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map));
     MOCK_METHOD1(OnReadRssiComplete, void(uint8_t rssi));
     MOCK_METHOD2(OnReadClockComplete, void(uint32_t clock, uint16_t accuracy));
+    MOCK_METHOD1(OnMasterLinkKeyComplete, void(KeyFlag flag));
+    MOCK_METHOD1(OnRoleChange, void(Role new_role));
+    MOCK_METHOD1(OnDisconnection, void(ErrorCode reason));
   } mock_connection_management_callbacks_;
 };
 
@@ -437,9 +499,7 @@
   fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
   fake_registry_.SynchronizeModuleHandler(&AclManager::Factory, std::chrono::milliseconds(20));
   fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
-  auto last_command = test_hci_layer_->GetLastCommand();
-  auto packet = GetPacketView(std::move(last_command));
-  CommandPacketView command = CommandPacketView::Create(packet);
+  CommandPacketView command = CommandPacketView::Create(test_hci_layer_->GetLastCommand());
   EXPECT_TRUE(command.IsValid());
   OpCode op_code = command.GetOpCode();
   EXPECT_EQ(op_code, OpCode::REJECT_CONNECTION_REQUEST);
@@ -452,10 +512,10 @@
   acl_manager_->CreateConnection(remote);
 
   // Wait for the connection request
-  std::unique_ptr<CommandPacketBuilder> last_command;
-  do {
-    last_command = test_hci_layer_->GetLastCommand();
-  } while (last_command == nullptr);
+  auto last_command = test_hci_layer_->GetCommandPacket(OpCode::CREATE_CONNECTION);
+  while (!last_command.IsValid()) {
+    last_command = test_hci_layer_->GetCommandPacket(OpCode::CREATE_CONNECTION);
+  }
 
   auto first_connection = GetConnectionFuture();
 
@@ -465,7 +525,7 @@
   auto first_connection_status = first_connection.wait_for(kTimeout);
   ASSERT_EQ(first_connection_status, std::future_status::ready);
 
-  std::shared_ptr<AclConnection> connection = GetLastConnection();
+  auto connection = GetLastConnection();
   ASSERT_EQ(connection->GetAddress(), remote);
 }
 
@@ -476,10 +536,10 @@
   acl_manager_->CreateConnection(remote);
 
   // Wait for the connection request
-  std::unique_ptr<CommandPacketBuilder> last_command;
-  do {
-    last_command = test_hci_layer_->GetLastCommand();
-  } while (last_command == nullptr);
+  auto last_command = test_hci_layer_->GetCommandPacket(OpCode::CREATE_CONNECTION);
+  while (!last_command.IsValid()) {
+    last_command = test_hci_layer_->GetCommandPacket(OpCode::CREATE_CONNECTION);
+  }
 
   EXPECT_CALL(mock_connection_callback_, OnConnectFail(remote, ErrorCode::PAGE_TIMEOUT));
   test_hci_layer_->IncomingEvent(
@@ -489,42 +549,86 @@
   fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
 }
 
-TEST_F(AclManagerTest, invoke_registered_callback_le_connection_complete_success) {
-  AddressWithType remote_with_type(remote, AddressType::PUBLIC_DEVICE_ADDRESS);
-  test_hci_layer_->SetCommandFuture();
-  acl_manager_->CreateLeConnection(remote_with_type);
+class AclManagerWithLeConnectionTest : public AclManagerTest {
+ protected:
+  void SetUp() override {
+    AclManagerTest::SetUp();
 
-  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION);
-  auto le_connection_management_command_view = LeConnectionManagementCommandView::Create(packet);
-  auto command_view = LeCreateConnectionView::Create(le_connection_management_command_view);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetPeerAddress(), remote);
-  EXPECT_EQ(command_view.GetPeerAddressType(), AddressType::PUBLIC_DEVICE_ADDRESS);
+    remote_with_type_ = AddressWithType(remote, AddressType::PUBLIC_DEVICE_ADDRESS);
+    test_hci_layer_->SetCommandFuture();
+    acl_manager_->CreateLeConnection(remote_with_type_);
+    test_hci_layer_->GetCommandPacket(OpCode::LE_ADD_DEVICE_TO_CONNECT_LIST);
+    test_hci_layer_->IncomingEvent(LeAddDeviceToConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+    test_hci_layer_->SetCommandFuture();
+    auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION);
+    auto le_connection_management_command_view = LeConnectionManagementCommandView::Create(packet);
+    auto command_view = LeCreateConnectionView::Create(le_connection_management_command_view);
+    ASSERT_TRUE(command_view.IsValid());
+    EXPECT_EQ(command_view.GetPeerAddress(), remote);
+    EXPECT_EQ(command_view.GetPeerAddressType(), AddressType::PUBLIC_DEVICE_ADDRESS);
 
-  test_hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01));
+    test_hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01));
 
-  auto first_connection = GetLeConnectionFuture();
+    auto first_connection = GetLeConnectionFuture();
 
-  test_hci_layer_->IncomingLeMetaEvent(
-      LeConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, 0x123, Role::SLAVE, AddressType::PUBLIC_DEVICE_ADDRESS,
-                                          remote, 0x0100, 0x0010, 0x0011, MasterClockAccuracy::PPM_30));
+    test_hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create(
+        ErrorCode::SUCCESS, handle_, Role::SLAVE, AddressType::PUBLIC_DEVICE_ADDRESS, remote, 0x0100, 0x0010, 0x0011,
+        ClockAccuracy::PPM_30));
 
-  auto first_connection_status = first_connection.wait_for(kTimeout);
-  ASSERT_EQ(first_connection_status, std::future_status::ready);
+    test_hci_layer_->SetCommandFuture();
+    test_hci_layer_->GetCommandPacket(OpCode::LE_REMOVE_DEVICE_FROM_CONNECT_LIST);
+    test_hci_layer_->IncomingEvent(LeRemoveDeviceFromConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
 
-  std::shared_ptr<AclConnection> connection = GetLastLeConnection();
-  ASSERT_EQ(connection->GetAddress(), remote);
+    auto first_connection_status = first_connection.wait_for(kTimeout);
+    ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+    connection_ = GetLastLeConnection();
+  }
+
+  void TearDown() override {
+    fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
+    fake_registry_.SynchronizeModuleHandler(&AclManager::Factory, std::chrono::milliseconds(20));
+    fake_registry_.StopAll();
+  }
+
+  void sync_client_handler() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    client_handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    auto future_status = future.wait_for(std::chrono::seconds(1));
+    EXPECT_EQ(future_status, std::future_status::ready);
+  }
+
+  uint16_t handle_ = 0x123;
+  std::shared_ptr<LeAclConnection> connection_;
+  AddressWithType remote_with_type_;
+
+  class MockLeConnectionManagementCallbacks : public LeConnectionManagementCallbacks {
+   public:
+    MOCK_METHOD1(OnDisconnection, void(ErrorCode reason));
+    MOCK_METHOD3(OnConnectionUpdate,
+                 void(uint16_t connection_interval, uint16_t connection_latency, uint16_t supervision_timeout));
+  } mock_le_connection_management_callbacks_;
+};
+
+// TODO: implement version of this test where controller supports Extended Advertising Feature in
+// GetControllerLeLocalSupportedFeatures, and LE Extended Create Connection is used
+TEST_F(AclManagerWithLeConnectionTest, invoke_registered_callback_le_connection_complete_success) {
+  ASSERT_EQ(connection_->GetLocalAddress(), my_initiating_address);
+  ASSERT_EQ(connection_->GetRemoteAddress(), remote_with_type_);
 }
 
 TEST_F(AclManagerTest, invoke_registered_callback_le_connection_complete_fail) {
   AddressWithType remote_with_type(remote, AddressType::PUBLIC_DEVICE_ADDRESS);
   test_hci_layer_->SetCommandFuture();
   acl_manager_->CreateLeConnection(remote_with_type);
-
+  test_hci_layer_->GetCommandPacket(OpCode::LE_ADD_DEVICE_TO_CONNECT_LIST);
+  test_hci_layer_->IncomingEvent(LeAddDeviceToConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  test_hci_layer_->SetCommandFuture();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION);
   auto le_connection_management_command_view = LeConnectionManagementCommandView::Create(packet);
   auto command_view = LeCreateConnectionView::Create(le_connection_management_command_view);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
   EXPECT_EQ(command_view.GetPeerAddress(), remote);
   EXPECT_EQ(command_view.GetPeerAddressType(), AddressType::PUBLIC_DEVICE_ADDRESS);
 
@@ -534,157 +638,53 @@
               OnLeConnectFail(remote_with_type, ErrorCode::CONNECTION_REJECTED_LIMITED_RESOURCES));
   test_hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create(
       ErrorCode::CONNECTION_REJECTED_LIMITED_RESOURCES, 0x123, Role::SLAVE, AddressType::PUBLIC_DEVICE_ADDRESS, remote,
-      0x0100, 0x0010, 0x0011, MasterClockAccuracy::PPM_30));
+      0x0100, 0x0010, 0x0011, ClockAccuracy::PPM_30));
 }
 
-TEST_F(AclManagerTest, invoke_registered_callback_le_connection_update_success) {
+TEST_F(AclManagerTest, cancel_le_connection) {
   AddressWithType remote_with_type(remote, AddressType::PUBLIC_DEVICE_ADDRESS);
   test_hci_layer_->SetCommandFuture();
   acl_manager_->CreateLeConnection(remote_with_type);
+  test_hci_layer_->GetCommandPacket(OpCode::LE_ADD_DEVICE_TO_CONNECT_LIST);
+  test_hci_layer_->IncomingEvent(LeAddDeviceToConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  test_hci_layer_->SetCommandFuture();
+  test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION);
 
-  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION);
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->CancelLeConnect(remote_with_type);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION_CANCEL);
   auto le_connection_management_command_view = LeConnectionManagementCommandView::Create(packet);
-  auto command_view = LeCreateConnectionView::Create(le_connection_management_command_view);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetPeerAddress(), remote);
-  EXPECT_EQ(command_view.GetPeerAddressType(), AddressType::PUBLIC_DEVICE_ADDRESS);
+  auto command_view = LeCreateConnectionCancelView::Create(le_connection_management_command_view);
+  ASSERT_TRUE(command_view.IsValid());
 
-  test_hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01));
-
-  auto first_connection = GetLeConnectionFuture();
-
-  test_hci_layer_->IncomingLeMetaEvent(
-      LeConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, 0x123, Role::SLAVE, AddressType::PUBLIC_DEVICE_ADDRESS,
-                                          remote, 0x0100, 0x0010, 0x0011, MasterClockAccuracy::PPM_30));
-
-  auto first_connection_status = first_connection.wait_for(kTimeout);
-  ASSERT_EQ(first_connection_status, std::future_status::ready);
-
-  std::shared_ptr<AclConnection> connection = GetLastLeConnection();
-  ASSERT_EQ(connection->GetAddress(), remote);
-
-  std::promise<ErrorCode> promise;
-  auto future = promise.get_future();
-  connection->LeConnectionUpdate(
-      0x0006, 0x0C80, 0x0000, 0x000A,
-      common::BindOnce([](std::promise<ErrorCode> promise, ErrorCode code) { promise.set_value(code); },
-                       std::move(promise)),
-      client_handler_);
-  test_hci_layer_->IncomingLeMetaEvent(
-      LeConnectionUpdateCompleteBuilder::Create(ErrorCode::SUCCESS, 0x123, 0x0006, 0x0000, 0x000A));
-  EXPECT_EQ(future.wait_for(std::chrono::milliseconds(3)), std::future_status::ready);
-  EXPECT_EQ(future.get(), ErrorCode::SUCCESS);
-}
-
-TEST_F(AclManagerTest, invoke_registered_callback_disconnection_complete) {
-  uint16_t handle = 0x123;
+  test_hci_layer_->IncomingEvent(LeCreateConnectionCancelCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  test_hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create(
+      ErrorCode::UNKNOWN_CONNECTION,
+      0x123,
+      Role::SLAVE,
+      AddressType::PUBLIC_DEVICE_ADDRESS,
+      remote,
+      0x0100,
+      0x0010,
+      0x0011,
+      ClockAccuracy::PPM_30));
 
   test_hci_layer_->SetCommandFuture();
-  acl_manager_->CreateConnection(remote);
+  packet = test_hci_layer_->GetCommandPacket(OpCode::LE_REMOVE_DEVICE_FROM_CONNECT_LIST);
+  le_connection_management_command_view = LeConnectionManagementCommandView::Create(packet);
+  auto remove_command_view = LeRemoveDeviceFromConnectListView::Create(le_connection_management_command_view);
+  ASSERT_TRUE(remove_command_view.IsValid());
 
-  // Wait for the connection request
-  std::unique_ptr<CommandPacketBuilder> last_command;
-  do {
-    last_command = test_hci_layer_->GetLastCommand();
-  } while (last_command == nullptr);
-
-  auto first_connection = GetConnectionFuture();
-
-  test_hci_layer_->IncomingEvent(
-      ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, remote, LinkType::ACL, Enable::DISABLED));
-
-  auto first_connection_status = first_connection.wait_for(kTimeout);
-  ASSERT_EQ(first_connection_status, std::future_status::ready);
-
-  std::shared_ptr<AclConnection> connection = GetLastConnection();
-
-  // Register the disconnect handler
-  std::promise<ErrorCode> promise;
-  auto future = promise.get_future();
-  connection->RegisterDisconnectCallback(
-      common::BindOnce([](std::promise<ErrorCode> promise, ErrorCode reason) { promise.set_value(reason); },
-                       std::move(promise)),
-      client_handler_);
-
-  test_hci_layer_->IncomingEvent(
-      DisconnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, ErrorCode::REMOTE_USER_TERMINATED_CONNECTION));
-
-  auto disconnection_status = future.wait_for(kTimeout);
-  ASSERT_EQ(disconnection_status, std::future_status::ready);
-  ASSERT_EQ(ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, future.get());
-
-  fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
+  test_hci_layer_->IncomingEvent(LeRemoveDeviceFromConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
 }
 
-TEST_F(AclManagerTest, acl_connection_finish_after_disconnected) {
-  uint16_t handle = 0x123;
-
-  test_hci_layer_->SetCommandFuture();
-  acl_manager_->CreateConnection(remote);
-
-  // Wait for the connection request
-  std::unique_ptr<CommandPacketBuilder> last_command;
-  do {
-    last_command = test_hci_layer_->GetLastCommand();
-  } while (last_command == nullptr);
-
-  auto first_connection = GetConnectionFuture();
-
-  test_hci_layer_->IncomingEvent(
-      ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, remote, LinkType::ACL, Enable::DISABLED));
-
-  auto first_connection_status = first_connection.wait_for(kTimeout);
-  ASSERT_EQ(first_connection_status, std::future_status::ready);
-
-  std::shared_ptr<AclConnection> connection = GetLastConnection();
-
-  // Register the disconnect handler
-  std::promise<ErrorCode> promise;
-  auto future = promise.get_future();
-  connection->RegisterDisconnectCallback(
-      common::BindOnce([](std::promise<ErrorCode> promise, ErrorCode reason) { promise.set_value(reason); },
-                       std::move(promise)),
-      client_handler_);
-
-  test_hci_layer_->IncomingEvent(DisconnectionCompleteBuilder::Create(
-      ErrorCode::SUCCESS, handle, ErrorCode::REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF));
-
-  auto disconnection_status = future.wait_for(kTimeout);
-  ASSERT_EQ(disconnection_status, std::future_status::ready);
-  ASSERT_EQ(ErrorCode::REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF, future.get());
-
-  connection->Finish();
-}
-
-TEST_F(AclManagerTest, acl_send_data_one_connection) {
-  uint16_t handle = 0x123;
-
-  acl_manager_->CreateConnection(remote);
-
-  // Wait for the connection request
-  std::unique_ptr<CommandPacketBuilder> last_command;
-  do {
-    last_command = test_hci_layer_->GetLastCommand();
-  } while (last_command == nullptr);
-
-  auto first_connection = GetConnectionFuture();
-
-  test_hci_layer_->IncomingEvent(
-      ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, remote, LinkType::ACL, Enable::DISABLED));
-
-  auto first_connection_status = first_connection.wait_for(kTimeout);
-  ASSERT_EQ(first_connection_status, std::future_status::ready);
-
-  std::shared_ptr<AclConnection> connection = GetLastConnection();
-
-  // Register the disconnect handler
-  connection->RegisterDisconnectCallback(
-      common::Bind([](std::shared_ptr<AclConnection> conn, ErrorCode) { conn->Finish(); }, connection),
-      client_handler_);
+TEST_F(AclManagerWithLeConnectionTest, acl_send_data_one_le_connection) {
+  ASSERT_EQ(connection_->GetRemoteAddress(), remote_with_type_);
+  ASSERT_EQ(connection_->GetHandle(), handle_);
 
   // Send a packet from HCI
-  test_hci_layer_->IncomingAclData(handle);
-  auto queue_end = connection->GetAclQueueEnd();
+  test_hci_layer_->IncomingAclData(handle_);
+  auto queue_end = connection_->GetAclQueueEnd();
 
   std::unique_ptr<PacketView<kLittleEndian>> received;
   do {
@@ -694,60 +694,128 @@
   PacketView<kLittleEndian> received_packet = *received;
 
   // Send a packet from the connection
-  SendAclData(handle, connection);
+  SendAclData(handle_, connection_->GetAclQueueEnd());
 
   auto sent_packet = test_hci_layer_->OutgoingAclData();
 
   // Send another packet from the connection
-  SendAclData(handle, connection);
+  SendAclData(handle_, connection_->GetAclQueueEnd());
 
   sent_packet = test_hci_layer_->OutgoingAclData();
-  connection->Disconnect(DisconnectReason::AUTHENTICATION_FAILURE);
 }
 
-TEST_F(AclManagerTest, acl_send_data_credits) {
-  uint16_t handle = 0x123;
+TEST_F(AclManagerWithLeConnectionTest, invoke_registered_callback_le_connection_update_success) {
+  ASSERT_EQ(connection_->GetLocalAddress(), my_initiating_address);
+  ASSERT_EQ(connection_->GetRemoteAddress(), remote_with_type_);
+  ASSERT_EQ(connection_->GetHandle(), handle_);
+  connection_->RegisterCallbacks(&mock_le_connection_management_callbacks_, client_handler_);
 
-  acl_manager_->CreateConnection(remote);
+  std::promise<ErrorCode> promise;
+  uint16_t connection_interval_min = 0x0012;
+  uint16_t connection_interval_max = 0x0080;
+  uint16_t connection_interval = (connection_interval_max + connection_interval_min) / 2;
+  uint16_t connection_latency = 0x0001;
+  uint16_t supervision_timeout = 0x000A;
+  test_hci_layer_->SetCommandFuture();
+  connection_->LeConnectionUpdate(connection_interval_min, connection_interval_max, connection_latency,
+                                  supervision_timeout, 0x10, 0x20);
+  auto update_packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CONNECTION_UPDATE);
+  auto update_view = LeConnectionUpdateView::Create(LeConnectionManagementCommandView::Create(update_packet));
+  ASSERT_TRUE(update_view.IsValid());
+  EXPECT_EQ(update_view.GetConnectionHandle(), handle_);
+  EXPECT_CALL(mock_le_connection_management_callbacks_,
+              OnConnectionUpdate(connection_interval, connection_latency, supervision_timeout));
+  test_hci_layer_->IncomingLeMetaEvent(LeConnectionUpdateCompleteBuilder::Create(
+      ErrorCode::SUCCESS, handle_, connection_interval, connection_latency, supervision_timeout));
+}
 
-  // Wait for the connection request
-  std::unique_ptr<CommandPacketBuilder> last_command;
+TEST_F(AclManagerWithLeConnectionTest, invoke_registered_callback_le_disconnect) {
+  ASSERT_EQ(connection_->GetRemoteAddress(), remote_with_type_);
+  ASSERT_EQ(connection_->GetHandle(), handle_);
+  connection_->RegisterCallbacks(&mock_le_connection_management_callbacks_, client_handler_);
+
+  auto reason = ErrorCode::REMOTE_USER_TERMINATED_CONNECTION;
+  EXPECT_CALL(mock_le_connection_management_callbacks_, OnDisconnection(reason));
+  test_hci_layer_->Disconnect(handle_, reason);
+}
+
+TEST_F(AclManagerWithLeConnectionTest, DISABLED_invoke_registered_callback_le_disconnect_data_race) {
+  ASSERT_EQ(connection_->GetRemoteAddress(), remote_with_type_);
+  ASSERT_EQ(connection_->GetHandle(), handle_);
+  connection_->RegisterCallbacks(&mock_le_connection_management_callbacks_, client_handler_);
+
+  test_hci_layer_->IncomingAclData(handle_);
+  auto reason = ErrorCode::REMOTE_USER_TERMINATED_CONNECTION;
+  EXPECT_CALL(mock_le_connection_management_callbacks_, OnDisconnection(reason));
+  test_hci_layer_->Disconnect(handle_, reason);
+}
+
+TEST_F(AclManagerWithLeConnectionTest, invoke_registered_callback_le_queue_disconnect) {
+  auto reason = ErrorCode::REMOTE_USER_TERMINATED_CONNECTION;
+  test_hci_layer_->Disconnect(handle_, reason);
+  fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
+  fake_registry_.SynchronizeModuleHandler(&AclManager::Factory, std::chrono::milliseconds(20));
+
+  EXPECT_CALL(mock_le_connection_management_callbacks_, OnDisconnection(reason));
+  connection_->RegisterCallbacks(&mock_le_connection_management_callbacks_, client_handler_);
+  sync_client_handler();
+}
+
+TEST_F(AclManagerWithConnectionTest, invoke_registered_callback_disconnection_complete) {
+  auto reason = ErrorCode::REMOTE_USER_TERMINATED_CONNECTION;
+  EXPECT_CALL(mock_connection_management_callbacks_, OnDisconnection(reason));
+  test_hci_layer_->Disconnect(handle_, reason);
+}
+
+TEST_F(AclManagerWithConnectionTest, acl_send_data_one_connection) {
+  // Send a packet from HCI
+  test_hci_layer_->IncomingAclData(handle_);
+  auto queue_end = connection_->GetAclQueueEnd();
+
+  std::unique_ptr<PacketView<kLittleEndian>> received;
   do {
-    last_command = test_hci_layer_->GetLastCommand();
-  } while (last_command == nullptr);
+    received = queue_end->TryDequeue();
+  } while (received == nullptr);
 
-  auto first_connection = GetConnectionFuture();
-  test_hci_layer_->IncomingEvent(
-      ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, remote, LinkType::ACL, Enable::DISABLED));
+  PacketView<kLittleEndian> received_packet = *received;
 
-  auto first_connection_status = first_connection.wait_for(kTimeout);
-  ASSERT_EQ(first_connection_status, std::future_status::ready);
+  // Send a packet from the connection
+  SendAclData(handle_, connection_->GetAclQueueEnd());
 
-  std::shared_ptr<AclConnection> connection = GetLastConnection();
+  auto sent_packet = test_hci_layer_->OutgoingAclData();
 
-  // Register the disconnect handler
-  connection->RegisterDisconnectCallback(
-      common::BindOnce([](std::shared_ptr<AclConnection> conn, ErrorCode) { conn->Finish(); }, connection),
-      client_handler_);
+  // Send another packet from the connection
+  SendAclData(handle_, connection_->GetAclQueueEnd());
 
+  sent_packet = test_hci_layer_->OutgoingAclData();
+  test_hci_layer_->SetCommandFuture();
+  auto reason = ErrorCode::AUTHENTICATION_FAILURE;
+  EXPECT_CALL(mock_connection_management_callbacks_, OnDisconnection(reason));
+  connection_->Disconnect(DisconnectReason::AUTHENTICATION_FAILURE);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::DISCONNECT);
+  auto command_view = DisconnectView::Create(packet);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetConnectionHandle(), handle_);
+  test_hci_layer_->Disconnect(handle_, reason);
+}
+
+TEST_F(AclManagerWithConnectionTest, acl_send_data_credits) {
   // Use all the credits
   for (uint16_t credits = 0; credits < test_controller_->total_acl_buffers_; credits++) {
     // Send a packet from the connection
-    SendAclData(handle, connection);
+    SendAclData(handle_, connection_->GetAclQueueEnd());
 
     auto sent_packet = test_hci_layer_->OutgoingAclData();
   }
 
   // Send another packet from the connection
-  SendAclData(handle, connection);
+  SendAclData(handle_, connection_->GetAclQueueEnd());
 
   test_hci_layer_->AssertNoOutgoingAclData();
 
-  test_controller_->CompletePackets(handle, 1);
+  test_controller_->CompletePackets(handle_, 1);
 
   auto after_credits_sent_packet = test_hci_layer_->OutgoingAclData();
-
-  connection->Disconnect(DisconnectReason::AUTHENTICATION_FAILURE);
 }
 
 TEST_F(AclManagerWithConnectionTest, send_switch_role) {
@@ -755,39 +823,28 @@
   acl_manager_->SwitchRole(connection_->GetAddress(), Role::SLAVE);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::SWITCH_ROLE);
   auto command_view = SwitchRoleView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetBdAddr(), connection_->GetAddress());
-  EXPECT_EQ(command_view.GetRole(), Role::SLAVE);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetBdAddr(), connection_->GetAddress());
+  ASSERT_EQ(command_view.GetRole(), Role::SLAVE);
 
-  EXPECT_CALL(mock_acl_manager_callbacks_, OnRoleChange(connection_->GetAddress(), Role::SLAVE));
+  EXPECT_CALL(mock_connection_management_callbacks_, OnRoleChange(Role::SLAVE));
   test_hci_layer_->IncomingEvent(RoleChangeBuilder::Create(ErrorCode::SUCCESS, connection_->GetAddress(), Role::SLAVE));
 }
 
-TEST_F(AclManagerWithConnectionTest, send_read_default_link_policy_settings) {
-  test_hci_layer_->SetCommandFuture();
-  acl_manager_->ReadDefaultLinkPolicySettings();
-  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_DEFAULT_LINK_POLICY_SETTINGS);
-  auto command_view = ReadDefaultLinkPolicySettingsView::Create(packet);
-  ASSERT(command_view.IsValid());
-
-  test_hci_layer_->SetCommandFuture();
-  EXPECT_CALL(mock_acl_manager_callbacks_, OnReadDefaultLinkPolicySettingsComplete(0x07));
-  uint8_t num_packets = 1;
-  test_hci_layer_->IncomingEvent(
-      ReadDefaultLinkPolicySettingsCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, 0x07));
-}
-
 TEST_F(AclManagerWithConnectionTest, send_write_default_link_policy_settings) {
   test_hci_layer_->SetCommandFuture();
-  acl_manager_->WriteDefaultLinkPolicySettings(0x05);
+  uint16_t link_policy_settings = 0x05;
+  acl_manager_->WriteDefaultLinkPolicySettings(link_policy_settings);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::WRITE_DEFAULT_LINK_POLICY_SETTINGS);
   auto command_view = WriteDefaultLinkPolicySettingsView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetDefaultLinkPolicySettings(), 0x05);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetDefaultLinkPolicySettings(), 0x05);
 
   uint8_t num_packets = 1;
   test_hci_layer_->IncomingEvent(
       WriteDefaultLinkPolicySettingsCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS));
+
+  ASSERT_EQ(link_policy_settings, acl_manager_->ReadDefaultLinkPolicySettings());
 }
 
 TEST_F(AclManagerWithConnectionTest, send_change_connection_packet_type) {
@@ -795,8 +852,8 @@
   connection_->ChangeConnectionPacketType(0xEE1C);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::CHANGE_CONNECTION_PACKET_TYPE);
   auto command_view = ChangeConnectionPacketTypeView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetPacketType(), 0xEE1C);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetPacketType(), 0xEE1C);
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnConnectionPacketTypeChanged(0xEE1C));
   test_hci_layer_->IncomingEvent(ConnectionPacketTypeChangedBuilder::Create(ErrorCode::SUCCESS, handle_, 0xEE1C));
@@ -807,7 +864,7 @@
   connection_->AuthenticationRequested();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::AUTHENTICATION_REQUESTED);
   auto command_view = AuthenticationRequestedView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnAuthenticationComplete);
   test_hci_layer_->IncomingEvent(AuthenticationCompleteBuilder::Create(ErrorCode::SUCCESS, handle_));
@@ -818,7 +875,7 @@
   connection_->ReadClockOffset();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_CLOCK_OFFSET);
   auto command_view = ReadClockOffsetView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnReadClockOffsetComplete(0x0123));
   test_hci_layer_->IncomingEvent(ReadClockOffsetCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, 0x0123));
@@ -829,9 +886,9 @@
   connection_->HoldMode(0x0500, 0x0020);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::HOLD_MODE);
   auto command_view = HoldModeView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetHoldModeMaxInterval(), 0x0500);
-  EXPECT_EQ(command_view.GetHoldModeMinInterval(), 0x0020);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetHoldModeMaxInterval(), 0x0500);
+  ASSERT_EQ(command_view.GetHoldModeMinInterval(), 0x0020);
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnModeChange(Mode::HOLD, 0x0020));
   test_hci_layer_->IncomingEvent(ModeChangeBuilder::Create(ErrorCode::SUCCESS, handle_, Mode::HOLD, 0x0020));
@@ -842,11 +899,11 @@
   connection_->SniffMode(0x0500, 0x0020, 0x0040, 0x0014);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::SNIFF_MODE);
   auto command_view = SniffModeView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetSniffMaxInterval(), 0x0500);
-  EXPECT_EQ(command_view.GetSniffMinInterval(), 0x0020);
-  EXPECT_EQ(command_view.GetSniffAttempt(), 0x0040);
-  EXPECT_EQ(command_view.GetSniffTimeout(), 0x0014);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetSniffMaxInterval(), 0x0500);
+  ASSERT_EQ(command_view.GetSniffMinInterval(), 0x0020);
+  ASSERT_EQ(command_view.GetSniffAttempt(), 0x0040);
+  ASSERT_EQ(command_view.GetSniffTimeout(), 0x0014);
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnModeChange(Mode::SNIFF, 0x0028));
   test_hci_layer_->IncomingEvent(ModeChangeBuilder::Create(ErrorCode::SUCCESS, handle_, Mode::SNIFF, 0x0028));
@@ -857,7 +914,7 @@
   connection_->ExitSniffMode();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::EXIT_SNIFF_MODE);
   auto command_view = ExitSniffModeView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnModeChange(Mode::ACTIVE, 0x00));
   test_hci_layer_->IncomingEvent(ModeChangeBuilder::Create(ErrorCode::SUCCESS, handle_, Mode::ACTIVE, 0x00));
@@ -868,12 +925,12 @@
   connection_->QosSetup(ServiceType::BEST_EFFORT, 0x1234, 0x1233, 0x1232, 0x1231);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::QOS_SETUP);
   auto command_view = QosSetupView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetServiceType(), ServiceType::BEST_EFFORT);
-  EXPECT_EQ(command_view.GetTokenRate(), 0x1234);
-  EXPECT_EQ(command_view.GetPeakBandwidth(), 0x1233);
-  EXPECT_EQ(command_view.GetLatency(), 0x1232);
-  EXPECT_EQ(command_view.GetDelayVariation(), 0x1231);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetServiceType(), ServiceType::BEST_EFFORT);
+  ASSERT_EQ(command_view.GetTokenRate(), 0x1234);
+  ASSERT_EQ(command_view.GetPeakBandwidth(), 0x1233);
+  ASSERT_EQ(command_view.GetLatency(), 0x1232);
+  ASSERT_EQ(command_view.GetDelayVariation(), 0x1231);
 
   EXPECT_CALL(mock_connection_management_callbacks_,
               OnQosSetupComplete(ServiceType::BEST_EFFORT, 0x1234, 0x1233, 0x1232, 0x1231));
@@ -887,13 +944,13 @@
                                  0x1231);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::FLOW_SPECIFICATION);
   auto command_view = FlowSpecificationView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetFlowDirection(), FlowDirection::OUTGOING_FLOW);
-  EXPECT_EQ(command_view.GetServiceType(), ServiceType::BEST_EFFORT);
-  EXPECT_EQ(command_view.GetTokenRate(), 0x1234);
-  EXPECT_EQ(command_view.GetTokenBucketSize(), 0x1233);
-  EXPECT_EQ(command_view.GetPeakBandwidth(), 0x1232);
-  EXPECT_EQ(command_view.GetAccessLatency(), 0x1231);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetFlowDirection(), FlowDirection::OUTGOING_FLOW);
+  ASSERT_EQ(command_view.GetServiceType(), ServiceType::BEST_EFFORT);
+  ASSERT_EQ(command_view.GetTokenRate(), 0x1234);
+  ASSERT_EQ(command_view.GetTokenBucketSize(), 0x1233);
+  ASSERT_EQ(command_view.GetPeakBandwidth(), 0x1232);
+  ASSERT_EQ(command_view.GetAccessLatency(), 0x1231);
 
   EXPECT_CALL(mock_connection_management_callbacks_,
               OnFlowSpecificationComplete(FlowDirection::OUTGOING_FLOW, ServiceType::BEST_EFFORT, 0x1234, 0x1233,
@@ -908,7 +965,7 @@
   connection_->Flush();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::FLUSH);
   auto command_view = FlushView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnFlushOccurred());
   test_hci_layer_->IncomingEvent(FlushOccurredBuilder::Create(handle_));
@@ -919,7 +976,7 @@
   connection_->RoleDiscovery();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::ROLE_DISCOVERY);
   auto command_view = RoleDiscoveryView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnRoleDiscoveryComplete(Role::MASTER));
   uint8_t num_packets = 1;
@@ -932,7 +989,7 @@
   connection_->ReadLinkPolicySettings();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_LINK_POLICY_SETTINGS);
   auto command_view = ReadLinkPolicySettingsView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnReadLinkPolicySettingsComplete(0x07));
   uint8_t num_packets = 1;
@@ -945,8 +1002,8 @@
   connection_->WriteLinkPolicySettings(0x05);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::WRITE_LINK_POLICY_SETTINGS);
   auto command_view = WriteLinkPolicySettingsView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetLinkPolicySettings(), 0x05);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetLinkPolicySettings(), 0x05);
 
   uint8_t num_packets = 1;
   test_hci_layer_->IncomingEvent(
@@ -958,10 +1015,10 @@
   connection_->SniffSubrating(0x1234, 0x1235, 0x1236);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::SNIFF_SUBRATING);
   auto command_view = SniffSubratingView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetMaximumLatency(), 0x1234);
-  EXPECT_EQ(command_view.GetMinimumRemoteTimeout(), 0x1235);
-  EXPECT_EQ(command_view.GetMinimumLocalTimeout(), 0x1236);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetMaximumLatency(), 0x1234);
+  ASSERT_EQ(command_view.GetMinimumRemoteTimeout(), 0x1235);
+  ASSERT_EQ(command_view.GetMinimumLocalTimeout(), 0x1236);
 
   uint8_t num_packets = 1;
   test_hci_layer_->IncomingEvent(SniffSubratingCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_));
@@ -972,7 +1029,7 @@
   connection_->ReadAutomaticFlushTimeout();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_AUTOMATIC_FLUSH_TIMEOUT);
   auto command_view = ReadAutomaticFlushTimeoutView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnReadAutomaticFlushTimeoutComplete(0x07ff));
   uint8_t num_packets = 1;
@@ -985,8 +1042,8 @@
   connection_->WriteAutomaticFlushTimeout(0x07FF);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::WRITE_AUTOMATIC_FLUSH_TIMEOUT);
   auto command_view = WriteAutomaticFlushTimeoutView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetFlushTimeout(), 0x07FF);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetFlushTimeout(), 0x07FF);
 
   uint8_t num_packets = 1;
   test_hci_layer_->IncomingEvent(
@@ -998,8 +1055,8 @@
   connection_->ReadTransmitPowerLevel(TransmitPowerLevelType::CURRENT);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_TRANSMIT_POWER_LEVEL);
   auto command_view = ReadTransmitPowerLevelView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetType(), TransmitPowerLevelType::CURRENT);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetType(), TransmitPowerLevelType::CURRENT);
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnReadTransmitPowerLevelComplete(0x07));
   uint8_t num_packets = 1;
@@ -1012,7 +1069,7 @@
   connection_->ReadLinkSupervisionTimeout();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_LINK_SUPERVISION_TIMEOUT);
   auto command_view = ReadLinkSupervisionTimeoutView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnReadLinkSupervisionTimeoutComplete(0x5677));
   uint8_t num_packets = 1;
@@ -1025,8 +1082,8 @@
   connection_->WriteLinkSupervisionTimeout(0x5678);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::WRITE_LINK_SUPERVISION_TIMEOUT);
   auto command_view = WriteLinkSupervisionTimeoutView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetLinkSupervisionTimeout(), 0x5678);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetLinkSupervisionTimeout(), 0x5678);
 
   uint8_t num_packets = 1;
   test_hci_layer_->IncomingEvent(
@@ -1038,7 +1095,7 @@
   connection_->ReadFailedContactCounter();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_FAILED_CONTACT_COUNTER);
   auto command_view = ReadFailedContactCounterView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnReadFailedContactCounterComplete(0x00));
   uint8_t num_packets = 1;
@@ -1051,7 +1108,7 @@
   connection_->ResetFailedContactCounter();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::RESET_FAILED_CONTACT_COUNTER);
   auto command_view = ResetFailedContactCounterView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   uint8_t num_packets = 1;
   test_hci_layer_->IncomingEvent(
@@ -1063,7 +1120,7 @@
   connection_->ReadLinkQuality();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_LINK_QUALITY);
   auto command_view = ReadLinkQualityView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnReadLinkQualityComplete(0xa9));
   uint8_t num_packets = 1;
@@ -1076,7 +1133,7 @@
   connection_->ReadAfhChannelMap();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_AFH_CHANNEL_MAP);
   auto command_view = ReadAfhChannelMapView::Create(packet);
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
   std::array<uint8_t, 10> afh_channel_map = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
 
   EXPECT_CALL(mock_connection_management_callbacks_,
@@ -1091,8 +1148,8 @@
   connection_->ReadRssi();
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_RSSI);
   auto command_view = ReadRssiView::Create(packet);
-  ASSERT(command_view.IsValid());
-
+  ASSERT_TRUE(command_view.IsValid());
+  sync_client_handler();
   EXPECT_CALL(mock_connection_management_callbacks_, OnReadRssiComplete(0x00));
   uint8_t num_packets = 1;
   test_hci_layer_->IncomingEvent(ReadRssiCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, 0x00));
@@ -1103,8 +1160,8 @@
   connection_->ReadClock(WhichClock::LOCAL);
   auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_CLOCK);
   auto command_view = ReadClockView::Create(packet);
-  ASSERT(command_view.IsValid());
-  EXPECT_EQ(command_view.GetWhichClock(), WhichClock::LOCAL);
+  ASSERT_TRUE(command_view.IsValid());
+  ASSERT_EQ(command_view.GetWhichClock(), WhichClock::LOCAL);
 
   EXPECT_CALL(mock_connection_management_callbacks_, OnReadClockComplete(0x00002e6a, 0x0000));
   uint8_t num_packets = 1;
@@ -1113,5 +1170,6 @@
 }
 
 }  // namespace
+}  // namespace acl_manager
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/address.cc b/gd/hci/address.cc
index db0695d..a8705d1 100644
--- a/gd/hci/address.cc
+++ b/gd/hci/address.cc
@@ -23,6 +23,8 @@
 #include <sstream>
 #include <vector>
 
+#include "os/log.h"
+
 namespace bluetooth {
 namespace hci {
 
@@ -31,6 +33,8 @@
 const Address Address::kAny{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
 const Address Address::kEmpty{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
 
+// Address cannot initialize member variables as it is a POD type
+// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
 Address::Address(const uint8_t (&addr)[6]) {
   std::copy(addr, addr + kLength, address);
 };
@@ -44,7 +48,7 @@
 }
 
 bool Address::FromString(const std::string& from, Address& to) {
-  Address new_addr;
+  Address new_addr{};
   if (from.length() != 17) {
     return false;
   }
@@ -84,7 +88,7 @@
 };
 
 bool Address::IsValidAddress(const std::string& address) {
-  Address tmp;
+  Address tmp{};
   return Address::FromString(address, tmp);
 }
 
diff --git a/gd/hci/address_pybind11_type_caster.h b/gd/hci/address_pybind11_type_caster.h
new file mode 100644
index 0000000..18151c3
--- /dev/null
+++ b/gd/hci/address_pybind11_type_caster.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <cstring>
+#include <string>
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+#include "hci/address.h"
+
+namespace py = pybind11;
+
+namespace pybind11 {
+namespace detail {
+template <>
+struct type_caster<::bluetooth::hci::Address> {
+ public:
+  // Set the Python name of Address and declare "value"
+  PYBIND11_TYPE_CASTER(::bluetooth::hci::Address, _("Address"));
+
+  // Convert from Python->C++
+  bool load(handle src, bool) {
+    PyObject* source = src.ptr();
+    if (py::isinstance<py::str>(src)) {
+      bool conversion_successful = bluetooth::hci::Address::FromString(PyUnicode_AsUTF8(source), value);
+      return conversion_successful && !PyErr_Occurred();
+    }
+    return false;
+  }
+
+  // Convert from C++->Python
+  static handle cast(bluetooth::hci::Address src, return_value_policy, handle) {
+    return PyUnicode_FromString(src.ToString().c_str());
+  }
+};
+}  // namespace detail
+}  // namespace pybind11
diff --git a/gd/hci/address_with_type.h b/gd/hci/address_with_type.h
index bed2cf2..ca77e2d 100644
--- a/gd/hci/address_with_type.h
+++ b/gd/hci/address_with_type.h
@@ -22,6 +22,7 @@
 #include <string>
 #include <utility>
 
+#include "crypto_toolbox/crypto_toolbox.h"
 #include "hci/address.h"
 #include "hci/hci_packets.h"
 
@@ -42,6 +43,34 @@
     return address_type_;
   }
 
+  /* Is this an Resolvable Private Address ? */
+  inline bool IsRpa() const {
+    return address_type_ == hci::AddressType::RANDOM_DEVICE_ADDRESS && ((address_.address)[0] & 0xc0) == 0x40;
+  }
+
+  /* Is this an Resolvable Private Address, that was generated from given irk ? */
+  bool IsRpaThatMatchesIrk(const crypto_toolbox::Octet16& irk) const {
+    if (!IsRpa()) return false;
+
+    /* use the 3 MSB of bd address as prand */
+    uint8_t prand[3];
+    prand[0] = address_.address[2];
+    prand[1] = address_.address[1];
+    prand[2] = address_.address[0];
+    /* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */
+    crypto_toolbox::Octet16 computed_hash = crypto_toolbox::aes_128(irk, &prand[0], 3);
+    uint8_t hash[3];
+    hash[0] = address_.address[5];
+    hash[1] = address_.address[4];
+    hash[2] = address_.address[3];
+    if (memcmp(computed_hash.data(), &hash[0], 3) == 0) {
+      // match
+      return true;
+    }
+    // not a match
+    return false;
+  }
+
   bool operator<(const AddressWithType& rhs) const {
     return address_ < rhs.address_ && address_type_ < rhs.address_type_;
   }
diff --git a/gd/hci/address_with_type_test.cc b/gd/hci/address_with_type_test.cc
index f1e5a60..37b4ab6 100644
--- a/gd/hci/address_with_type_test.cc
+++ b/gd/hci/address_with_type_test.cc
@@ -64,5 +64,38 @@
   EXPECT_NE(hasher(address_with_type_1), hasher(address_with_type_2));
 }
 
+TEST(AddressWithTypeTest, IsRpa) {
+  // Public address can't be RPA
+  EXPECT_FALSE(
+      AddressWithType(Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::PUBLIC_IDENTITY_ADDRESS).IsRpa());
+
+  // Must have proper Most Significant Bit configuration
+  EXPECT_FALSE(
+      AddressWithType(Address{{0x30, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_TRUE(
+      AddressWithType(Address{{0x40, 0x02, 0x03, 0x04, 0x05, 0x03}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_TRUE(
+      AddressWithType(Address{{0x50, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_TRUE(
+      AddressWithType(Address{{0x60, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_TRUE(
+      AddressWithType(Address{{0x70, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_FALSE(
+      AddressWithType(Address{{0x80, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+}
+
+TEST(AddressWithTypeTest, IsRpaThatMatchesIrk) {
+  // Public address can't be RPA
+  AddressWithType address_1 =
+      AddressWithType(Address{{0x50, 0x02, 0x03, 0xC9, 0x12, 0xDE}}, AddressType::RANDOM_DEVICE_ADDRESS);
+  AddressWithType address_2 =
+      AddressWithType(Address{{0x50, 0x02, 0x03, 0xC9, 0x12, 0xDD}}, AddressType::RANDOM_DEVICE_ADDRESS);
+  crypto_toolbox::Octet16 irk_1{0x90, 0x5e, 0x60, 0x59, 0xc9, 0x11, 0x43, 0x7b,
+                                0x04, 0x09, 0x6a, 0x53, 0x28, 0xe6, 0x59, 0x6d};
+
+  EXPECT_TRUE(address_1.IsRpaThatMatchesIrk(irk_1));
+  EXPECT_FALSE(address_2.IsRpaThatMatchesIrk(irk_1));
+}
+
 }  // namespace hci
 }  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/hci/cert/acl_manager_test.py b/gd/hci/cert/acl_manager_test.py
new file mode 100644
index 0000000..7f059c4
--- /dev/null
+++ b/gd/hci/cert/acl_manager_test.py
@@ -0,0 +1,100 @@
+#!/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.gd_base_test import GdBaseTestClass
+from cert.truth import assertThat
+from neighbor.facade import facade_pb2 as neighbor_facade
+from bluetooth_packets_python3 import hci_packets
+from cert.py_hci import PyHci
+from cert.py_acl_manager import PyAclManager
+
+
+class AclManagerTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI')
+
+    # todo: move into GdBaseTestClass, based on modules inited
+    def setup_test(self):
+        super().setup_test()
+        self.cert_hci = PyHci(self.cert, acl_streaming=True)
+        self.dut_acl_manager = PyAclManager(self.dut)
+
+    def teardown_test(self):
+        self.cert_hci.close()
+        super().teardown_test()
+
+    def test_dut_connects(self):
+        self.cert_hci.enable_inquiry_and_page_scan()
+        cert_address = self.cert_hci.read_own_address()
+
+        self.dut_acl_manager.initiate_connection(cert_address)
+        cert_acl = self.cert_hci.accept_connection()
+        with self.dut_acl_manager.complete_outgoing_connection() as dut_acl:
+            cert_acl.send_first(b'\x26\x00\x07\x00This is just SomeAclData from the Cert')
+            dut_acl.send(b'\x29\x00\x07\x00This is just SomeMoreAclData from the DUT')
+
+            assertThat(cert_acl).emits(lambda packet: b'SomeMoreAclData' in packet.data)
+            assertThat(dut_acl).emits(lambda packet: b'SomeAclData' in packet.payload)
+
+    def test_cert_connects(self):
+        dut_address = self.dut.hci_controller.GetMacAddressSimple()
+        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
+
+        self.dut_acl_manager.listen_for_an_incoming_connection()
+        self.cert_hci.initiate_connection(dut_address)
+        with self.dut_acl_manager.complete_incoming_connection() as dut_acl:
+            cert_acl = self.cert_hci.complete_connection()
+
+            dut_acl.send(b'\x29\x00\x07\x00This is just SomeMoreAclData from the DUT')
+
+            cert_acl.send_first(b'\x26\x00\x07\x00This is just SomeAclData from the Cert')
+
+            assertThat(cert_acl).emits(lambda packet: b'SomeMoreAclData' in packet.data)
+            assertThat(dut_acl).emits(lambda packet: b'SomeAclData' in packet.payload)
+
+    def test_cert_connects_disconnects(self):
+        dut_address = self.dut.hci_controller.GetMacAddressSimple()
+        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
+
+        self.dut_acl_manager.listen_for_an_incoming_connection()
+        self.cert_hci.initiate_connection(dut_address)
+        with self.dut_acl_manager.complete_incoming_connection() as dut_acl:
+            cert_acl = self.cert_hci.complete_connection()
+
+            dut_acl.send(b'\x29\x00\x07\x00This is just SomeMoreAclData from the DUT')
+
+            cert_acl.send_first(b'\x26\x00\x07\x00This is just SomeAclData from the Cert')
+
+            assertThat(cert_acl).emits(lambda packet: b'SomeMoreAclData' in packet.data)
+            assertThat(dut_acl).emits(lambda packet: b'SomeAclData' in packet.payload)
+
+            dut_acl.disconnect(hci_packets.DisconnectReason.REMOTE_USER_TERMINATED_CONNECTION)
+            dut_acl.wait_for_disconnection_complete()
+
+    def test_recombination_l2cap_packet(self):
+        self.cert_hci.enable_inquiry_and_page_scan()
+        cert_address = self.cert_hci.read_own_address()
+
+        self.dut_acl_manager.initiate_connection(cert_address)
+        cert_acl = self.cert_hci.accept_connection()
+        with self.dut_acl_manager.complete_outgoing_connection() as dut_acl:
+            cert_acl.send_first(b'\x06\x00\x07\x00Hello')
+            cert_acl.send_continuing(b'!')
+            cert_acl.send_first(b'\xe8\x03\x07\x00' + b'Hello' * 200)
+
+            assertThat(dut_acl).emits(lambda packet: b'Hello!' in packet.payload,
+                                      lambda packet: b'Hello' * 200 in packet.payload).inOrder()
diff --git a/gd/hci/cert/api.proto b/gd/hci/cert/api.proto
deleted file mode 100644
index 30b3591..0000000
--- a/gd/hci/cert/api.proto
+++ /dev/null
@@ -1,46 +0,0 @@
-syntax = "proto3";
-
-package bluetooth.hci.cert;
-
-import "google/protobuf/empty.proto";
-import "facade/common.proto";
-
-service AclManagerCert {
-  rpc SetPageScanMode(PageScanMode) returns (google.protobuf.Empty) {}
-  rpc SetIncomingConnectionPolicy(IncomingConnectionPolicy) returns (google.protobuf.Empty) {}
-  rpc Connect(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc Disconnect(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc FetchConnectionComplete(google.protobuf.Empty) returns (stream ConnectionEvent) {}
-  rpc FetchDisconnection(google.protobuf.Empty) returns (stream DisconnectionEvent) {}
-  rpc FetchConnectionFailed(google.protobuf.Empty) returns (stream ConnectionFailedEvent) {}
-  rpc SendAclData(AclData) returns (google.protobuf.Empty) {}
-  rpc FetchAclData(google.protobuf.Empty) returns (stream AclData) {}
-}
-
-message PageScanMode {
-  bool enabled = 1;
-}
-
-message IncomingConnectionPolicy {
-  facade.BluetoothAddress remote = 1;
-  bool accepted = 2;
-}
-
-message ConnectionEvent {
-  facade.BluetoothAddress remote = 1;
-}
-
-message DisconnectionEvent {
-  facade.BluetoothAddress remote = 1;
-  uint32 reason = 2;
-}
-
-message ConnectionFailedEvent {
-  facade.BluetoothAddress remote = 1;
-  uint32 reason = 2;
-}
-
-message AclData {
-  facade.BluetoothAddress remote = 1;
-  bytes payload = 2;
-}
diff --git a/gd/hci/cert/cert.cc b/gd/hci/cert/cert.cc
deleted file mode 100644
index 996a7de..0000000
--- a/gd/hci/cert/cert.cc
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "hci/cert/cert.h"
-
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <set>
-
-#include "common/blocking_queue.h"
-#include "grpc/grpc_event_queue.h"
-#include "hci/cert/api.grpc.pb.h"
-#include "hci/classic_security_manager.h"
-#include "hci/controller.h"
-#include "hci/hci_layer.h"
-#include "hci/hci_packets.h"
-#include "os/queue.h"
-#include "packet/raw_builder.h"
-
-using ::grpc::ServerAsyncResponseWriter;
-using ::grpc::ServerAsyncWriter;
-using ::grpc::ServerContext;
-
-using ::bluetooth::common::Bind;
-using ::bluetooth::common::BindOnce;
-using ::bluetooth::packet::RawBuilder;
-
-namespace bluetooth {
-namespace hci {
-namespace cert {
-
-class AclManagerCertService : public AclManagerCert::Service {
- public:
-  AclManagerCertService(Controller* controller, HciLayer* hci_layer, ::bluetooth::os::Handler* facade_handler)
-      : controller_(controller), hci_layer_(hci_layer), handler_(facade_handler),
-        acl_queue_end_(hci_layer_->GetAclQueueEnd()) {
-    hci_layer_->RegisterEventHandler(EventCode::CONNECTION_COMPLETE,
-                                     Bind(&AclManagerCertService::on_connection_complete, common::Unretained(this)),
-                                     handler_);
-    hci_layer_->RegisterEventHandler(EventCode::DISCONNECTION_COMPLETE,
-                                     Bind(&AclManagerCertService::on_disconnection_complete, common::Unretained(this)),
-                                     handler_);
-    hci_layer_->RegisterEventHandler(EventCode::CONNECTION_REQUEST,
-                                     Bind(&AclManagerCertService::on_incoming_connection, common::Unretained(this)),
-                                     handler_);
-    hci_layer_->RegisterEventHandler(
-        EventCode::CONNECTION_PACKET_TYPE_CHANGED,
-        Bind(&AclManagerCertService::on_connection_packet_type_changed, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::QOS_SETUP_COMPLETE,
-                                     Bind(&AclManagerCertService::on_qos_setup_complete, common::Unretained(this)),
-                                     handler_);
-    hci_layer_->RegisterEventHandler(EventCode::ROLE_CHANGE,
-                                     Bind(&AclManagerCertService::on_role_change, common::Unretained(this)), handler_);
-
-    controller_->RegisterCompletedAclPacketsCallback(common::Bind([](uint16_t, uint16_t) { /* TODO check */ }),
-                                                     handler_);
-    acl_queue_end_->RegisterDequeue(handler_,
-                                    Bind(&AclManagerCertService::on_incoming_packet, common::Unretained(this)));
-  }
-
-  void on_incoming_packet() {
-    auto packet = acl_queue_end_->TryDequeue();
-    ASSERT(packet->IsValid());
-    AclData acl_data;
-    if (connected_devices_.find(packet->GetHandle()) == connected_devices_.end()) {
-      LOG_ERROR("Can't find remote device");
-      return;
-    }
-    auto address = connected_devices_[packet->GetHandle()];
-    acl_data.mutable_remote()->set_address(address.ToString());
-    std::string data = std::string(packet->begin(), packet->end());
-    acl_data.set_payload(data);
-    pending_acl_data_.OnIncomingEvent(acl_data);
-  }
-
-  ~AclManagerCertService() {
-    acl_queue_end_->UnregisterDequeue();
-    hci_layer_->UnregisterEventHandler(EventCode::CONNECTION_REQUEST);
-    hci_layer_->UnregisterEventHandler(EventCode::DISCONNECTION_COMPLETE);
-    hci_layer_->UnregisterEventHandler(EventCode::CONNECTION_COMPLETE);
-  }
-
-  void on_connection_complete(EventPacketView packet) {
-    ConnectionCompleteView connection_complete = ConnectionCompleteView::Create(std::move(packet));
-    ASSERT(connection_complete.IsValid());
-    auto status = connection_complete.GetStatus();
-    auto address = connection_complete.GetBdAddr();
-    auto handle = connection_complete.GetConnectionHandle();
-    if (status == ErrorCode::SUCCESS) {
-      connected_devices_.emplace(handle, address);
-      ConnectionEvent event;
-      event.mutable_remote()->set_address(address.ToString());
-      pending_connection_complete_.OnIncomingEvent(event);
-    } else {
-      ConnectionFailedEvent event;
-      event.mutable_remote()->set_address(address.ToString());
-      event.set_reason(static_cast<uint32_t>(connection_complete.GetStatus()));
-      pending_connection_failed_.OnIncomingEvent(event);
-    }
-  }
-
-  void on_disconnection_complete(EventPacketView packet) {
-    DisconnectionCompleteView disconnection_complete = DisconnectionCompleteView::Create(std::move(packet));
-    ASSERT(disconnection_complete.IsValid());
-    auto status = disconnection_complete.GetStatus();
-    auto handle = disconnection_complete.GetConnectionHandle();
-    auto device = connected_devices_.find(handle);
-
-    ASSERT(device != connected_devices_.end());
-    auto address = device->second;
-    if (status == ErrorCode::SUCCESS) {
-      connected_devices_.erase(handle);
-      DisconnectionEvent event;
-      event.mutable_remote()->set_address(address.ToString());
-      event.set_reason(static_cast<uint32_t>(disconnection_complete.GetReason()));
-      pending_disconnection_.OnIncomingEvent(event);
-    }
-  }
-
-  void on_incoming_connection(EventPacketView packet) {
-    ConnectionRequestView request = ConnectionRequestView::Create(packet);
-    ASSERT(request.IsValid());
-    Address address = request.GetBdAddr();
-    if (accepted_devices_.find(address) != accepted_devices_.end()) {
-      auto role = AcceptConnectionRequestRole::BECOME_MASTER;  // We prefer to be master
-      hci_layer_->EnqueueCommand(AcceptConnectionRequestBuilder::Create(address, role),
-                                 common::BindOnce([](CommandStatusView status) { /* TODO: check? */ }), handler_);
-    } else {
-      auto reason = RejectConnectionReason::LIMITED_RESOURCES;
-      auto builder = RejectConnectionRequestBuilder::Create(address, reason);
-      hci_layer_->EnqueueCommand(std::move(builder), BindOnce([](CommandStatusView status) { /* TODO: check? */ }),
-                                 handler_);
-    }
-  }
-
-  void on_connection_packet_type_changed(EventPacketView packet) { /*TODO*/
-  }
-
-  void on_qos_setup_complete(EventPacketView packet) { /*TODO*/
-  }
-
-  void on_role_change(EventPacketView packet) { /*TODO*/
-  }
-
-  ::grpc::Status SetPageScanMode(::grpc::ServerContext* context, const ::bluetooth::hci::cert::PageScanMode* request,
-                                 ::google::protobuf::Empty* response) override {
-    ScanEnable scan_enable = request->enabled() ? ScanEnable::PAGE_SCAN_ONLY : ScanEnable::NO_SCANS;
-    std::promise<void> promise;
-    auto future = promise.get_future();
-    hci_layer_->EnqueueCommand(
-        WriteScanEnableBuilder::Create(scan_enable),
-        common::BindOnce([](std::promise<void> promise, CommandCompleteView) { promise.set_value(); },
-                         std::move(promise)),
-        handler_);
-    future.wait();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SetIncomingConnectionPolicy(::grpc::ServerContext* context,
-                                             const ::bluetooth::hci::cert::IncomingConnectionPolicy* request,
-                                             ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    if (request->accepted()) {
-      accepted_devices_.insert(peer);
-    } else {
-      accepted_devices_.erase(peer);
-    }
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status Connect(::grpc::ServerContext* context, const facade::BluetoothAddress* remote,
-                         ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(mutex_);
-
-    uint16_t packet_type = 0x4408 /* DM 1,3,5 */ | 0x8810 /*DH 1,3,5 */;
-    PageScanRepetitionMode page_scan_repetition_mode = PageScanRepetitionMode::R1;
-    uint16_t clock_offset = 0;
-    ClockOffsetValid clock_offset_valid = ClockOffsetValid::INVALID;
-    CreateConnectionRoleSwitch allow_role_switch = CreateConnectionRoleSwitch::ALLOW_ROLE_SWITCH;
-
-    Address peer;
-    ASSERT(Address::FromString(remote->address(), peer));
-    std::unique_ptr<CreateConnectionBuilder> packet = CreateConnectionBuilder::Create(
-        peer, packet_type, page_scan_repetition_mode, clock_offset, clock_offset_valid, allow_role_switch);
-
-    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandStatusView status) {
-                                 ASSERT(status.IsValid());
-                                 ASSERT(status.GetCommandOpCode() == OpCode::CREATE_CONNECTION);
-                               }),
-                               handler_);
-
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status Disconnect(::grpc::ServerContext* context, const facade::BluetoothAddress* request,
-                            ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    Address::FromString(request->address(), peer);
-    uint16_t handle = find_connected_device_handle_by_address(peer);
-    if (handle == kInvalidHandle) {
-      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid address");
-    }
-
-    DisconnectReason reason = DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION;
-    std::unique_ptr<DisconnectBuilder> packet = DisconnectBuilder::Create(handle, reason);
-    hci_layer_->EnqueueCommand(std::move(packet), BindOnce([](CommandStatusView status) { /* TODO: check? */ }),
-                               handler_);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendAclData(::grpc::ServerContext* context, const AclData* request,
-                             ::google::protobuf::Empty* response) override {
-    Address peer;
-    Address::FromString(request->remote().address(), peer);
-    auto handle = find_connected_device_handle_by_address(peer);
-    if (handle == kInvalidHandle) {
-      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid address");
-    }
-
-    constexpr PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
-    constexpr BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
-    std::unique_ptr<RawBuilder> packet = std::make_unique<RawBuilder>();
-    auto req_string = request->payload();
-    packet->AddOctets(std::vector<uint8_t>(req_string.begin(), req_string.end()));
-    auto acl_packet = AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(packet));
-    acl_enqueue_buffer_.Enqueue(std::move(acl_packet), handler_);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status FetchAclData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                              ::grpc::ServerWriter<AclData>* writer) override {
-    return pending_acl_data_.RunLoop(context, writer);
-  }
-
-  ::grpc::Status FetchConnectionComplete(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                         ::grpc::ServerWriter<ConnectionEvent>* writer) override {
-    return pending_connection_complete_.RunLoop(context, writer);
-  };
-
-  ::grpc::Status FetchConnectionFailed(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                       ::grpc::ServerWriter<ConnectionFailedEvent>* writer) override {
-    return pending_connection_failed_.RunLoop(context, writer);
-  };
-
-  ::grpc::Status FetchDisconnection(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                    ::grpc::ServerWriter<DisconnectionEvent>* writer) override {
-    return pending_disconnection_.RunLoop(context, writer);
-  }
-
- private:
-  Controller* controller_;
-  HciLayer* hci_layer_;
-  ::bluetooth::os::Handler* handler_;
-  common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* acl_queue_end_;
-  os::EnqueueBuffer<AclPacketBuilder> acl_enqueue_buffer_{acl_queue_end_};
-  mutable std::mutex mutex_;
-  std::set<Address> accepted_devices_;
-  std::map<uint16_t /* handle */, Address> connected_devices_;
-  ::bluetooth::grpc::GrpcEventQueue<AclData> pending_acl_data_{"FetchAclData"};
-  ::bluetooth::grpc::GrpcEventQueue<ConnectionEvent> pending_connection_complete_{"FetchConnectionComplete"};
-  ::bluetooth::grpc::GrpcEventQueue<ConnectionFailedEvent> pending_connection_failed_{"FetchConnectionFailed"};
-  ::bluetooth::grpc::GrpcEventQueue<DisconnectionEvent> pending_disconnection_{"FetchDisconnection"};
-
-  constexpr static uint16_t kInvalidHandle = 0xffff;
-
-  uint16_t find_connected_device_handle_by_address(Address address) {
-    for (auto device : connected_devices_) {
-      if (device.second == address) {
-        return device.first;
-      }
-    }
-    return kInvalidHandle;  // Can't find
-  }
-};
-
-void AclManagerCertModule::ListDependencies(ModuleList* list) {
-  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
-  list->add<Controller>();
-  list->add<HciLayer>();
-  list->add<ClassicSecurityManager>();
-}
-
-void AclManagerCertModule::Start() {
-  ::bluetooth::grpc::GrpcFacadeModule::Start();
-  service_ = new AclManagerCertService(GetDependency<Controller>(), GetDependency<HciLayer>(), GetHandler());
-}
-
-void AclManagerCertModule::Stop() {
-  delete service_;
-  ::bluetooth::grpc::GrpcFacadeModule::Stop();
-}
-
-::grpc::Service* AclManagerCertModule::GetService() const {
-  return service_;
-}
-
-const ModuleFactory AclManagerCertModule::Factory =
-    ::bluetooth::ModuleFactory([]() { return new AclManagerCertModule(); });
-
-}  // namespace cert
-}  // namespace hci
-}  // namespace bluetooth
diff --git a/gd/hci/cert/controller_test.py b/gd/hci/cert/controller_test.py
new file mode 100644
index 0000000..dbc7793
--- /dev/null
+++ b/gd/hci/cert/controller_test.py
@@ -0,0 +1,55 @@
+#!/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.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
+
+
+class ControllerTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(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
+
+    def test_get_local_extended_features(self):
+        request = controller_facade.PageNumberMsg()
+        request.page_number = 1
+        dut_feature_response1 = self.dut.hci_controller.GetLocalExtendedFeatures(request)
+        request0 = controller_facade.PageNumberMsg()
+        request0.page_number = 0
+        dut_feature_response0 = self.dut.hci_controller.GetLocalExtendedFeatures(request0)
+
+        assertThat(dut_feature_response1.page).isNotEqualTo(dut_feature_response0.page)
+
+    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')
diff --git a/gd/hci/cert/direct_hci_test.py b/gd/hci/cert/direct_hci_test.py
new file mode 100644
index 0000000..f28e7cf
--- /dev/null
+++ b/gd/hci/cert/direct_hci_test.py
@@ -0,0 +1,398 @@
+#!/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.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 facade_pb2 as hci_facade
+from bluetooth_packets_python3 import hci_packets
+
+
+class DirectHciTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(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(hci_packets.ResetBuilder().Serialize())
+
+    def teardown_test(self):
+        self.dut_hci.close()
+        self.cert_hal.close()
+        super().teardown_test()
+
+    def send_hal_hci_command(self, command):
+        self.cert_hal.send_hci_command(bytes(command.Serialize()))
+
+    def enqueue_acl_data(self, handle, pb_flag, b_flag, acl):
+        acl_msg = hci_facade.AclMsg(
+            handle=int(handle), packet_boundary_flag=int(pb_flag), broadcast_flag=int(b_flag), data=acl)
+        self.dut.hci.SendAclData(acl_msg)
+
+    def send_hal_acl_data(self, handle, pb_flag, b_flag, acl):
+        lower = handle & 0xff
+        upper = (handle >> 8) & 0xf
+        upper = upper | int(pb_flag) & 0x3
+        upper = upper | ((int(b_flag) & 0x3) << 2)
+        lower_length = len(acl) & 0xff
+        upper_length = (len(acl) & 0xff00) >> 8
+        concatenated = bytes([lower, upper, lower_length, upper_length] + list(acl))
+        self.cert_hal.send_acl(concatenated)
+
+    def test_local_hci_cmd_and_event(self):
+        # Loopback mode responds with ACL and SCO connection complete
+        self.dut_hci.register_for_events(hci_packets.EventCode.LOOPBACK_COMMAND)
+
+        self.dut_hci.send_command_with_complete(
+            hci_packets.WriteLoopbackModeBuilder(hci_packets.LoopbackMode.ENABLE_LOCAL))
+
+        cmd2loop = hci_packets.ReadLocalNameBuilder()
+        self.dut_hci.send_command_with_complete(cmd2loop)
+
+        looped_bytes = bytes(cmd2loop.Serialize())
+        assertThat(self.dut_hci.get_event_stream()).emits(lambda packet: looped_bytes in packet.event)
+
+    def test_inquiry_from_dut(self):
+        self.dut_hci.register_for_events(hci_packets.EventCode.INQUIRY_RESULT)
+
+        self.send_hal_hci_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+        lap = hci_packets.Lap()
+        lap.lap = 0x33
+        self.dut_hci.send_command_with_status(hci_packets.InquiryBuilder(lap, 0x30, 0xff))
+        assertThat(self.dut_hci.get_event_stream()).emits(
+            HciMatchers.EventWithCode(hci_packets.EventCode.INQUIRY_RESULT))
+
+    def test_le_ad_scan_cert_advertises(self):
+        self.dut_hci.register_for_le_events(hci_packets.SubeventCode.EXTENDED_ADVERTISING_REPORT)
+        self.dut_hci.register_for_le_events(hci_packets.SubeventCode.ADVERTISING_REPORT)
+
+        # DUT Scans
+        self.dut_hci.send_command_with_complete(hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
+        phy_scan_params = hci_packets.PhyScanParameters()
+        phy_scan_params.le_scan_interval = 6553
+        phy_scan_params.le_scan_window = 6553
+        phy_scan_params.le_scan_type = hci_packets.LeScanType.ACTIVE
+
+        self.dut_hci.send_command_with_complete(
+            hci_packets.LeSetExtendedScanParametersBuilder(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                                                           hci_packets.LeScanningFilterPolicy.ACCEPT_ALL, 1,
+                                                           [phy_scan_params]))
+        self.dut_hci.send_command_with_complete(
+            hci_packets.LeSetExtendedScanEnableBuilder(hci_packets.Enable.ENABLED,
+                                                       hci_packets.FilterDuplicates.DISABLED, 0, 0))
+
+        # CERT Advertises
+        advertising_handle = 0
+        self.send_hal_hci_command(
+            hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                advertising_handle,
+                hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                512,
+                768,
+                7,
+                hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                'A6:A5:A4:A3:A2:A1',
+                hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                0xF7,
+                1,  # SID
+                hci_packets.Enable.DISABLED  # Scan request notification
+            ))
+
+        self.send_hal_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.send_hal_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.send_hal_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 = 0
+        enabled_set.duration = 0
+        enabled_set.max_extended_advertising_events = 0
+        self.send_hal_hci_command(
+            hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
+
+        assertThat(self.dut_hci.get_le_event_stream()).emits(lambda packet: b'Im_A_Cert' in packet.event)
+
+        self.send_hal_hci_command(
+            hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.DISABLED, [enabled_set]))
+        self.dut_hci.send_command_with_complete(
+            hci_packets.LeSetExtendedScanEnableBuilder(hci_packets.Enable.DISABLED,
+                                                       hci_packets.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 = 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
+        return phy_scan_params
+
+    def test_le_connection_dut_advertises(self):
+        self.dut_hci.register_for_le_events(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+        # Cert Connects
+        self.send_hal_hci_command(hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
+        phy_scan_params = DirectHciTest._create_phy_scan_params()
+        self.send_hal_hci_command(
+            hci_packets.LeExtendedCreateConnectionBuilder(
+                hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS, hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                hci_packets.AddressType.RANDOM_DEVICE_ADDRESS, '0D:05:04:03:02:01', 1, [phy_scan_params]))
+
+        # DUT Advertises
+        advertising_handle = 0
+        self.dut_hci.send_command_with_complete(
+            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.dut_hci.send_command_with_complete(
+            hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0D: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_The_DUT!'))  # TODO: Fix and remove !
+
+        self.dut_hci.send_command_with_complete(
+            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_The_D'))
+
+        self.dut_hci.send_command_with_complete(
+            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.dut_hci.send_command_with_complete(
+            hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
+
+        # Check for success of Enable
+        assertThat(self.dut_hci.get_event_stream()).emits(
+            HciMatchers.CommandComplete(hci_packets.OpCode.LE_SET_EXTENDED_ADVERTISING_ENABLE))
+
+        (dut_handle, cert_handle) = self._verify_le_connection_complete()
+
+        # Send ACL Data
+        self.enqueue_acl_data(dut_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                              hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeAclData'))
+        self.send_hal_acl_data(cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                               hci_packets.BroadcastFlag.POINT_TO_POINT, 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.data) or b'SomeMoreAclData' in packet.data)
+
+    def test_le_connect_list_connection_cert_advertises(self):
+        self.dut_hci.register_for_le_events(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+        # DUT Connects
+        self.dut_hci.send_command_with_complete(hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
+        self.dut_hci.send_command_with_complete(
+            hci_packets.LeAddDeviceToConnectListBuilder(hci_packets.ConnectListAddressType.RANDOM, '0C:05:04:03:02:01'))
+        phy_scan_params = DirectHciTest._create_phy_scan_params()
+        self.dut_hci.send_command_with_status(
+            hci_packets.LeExtendedCreateConnectionBuilder(
+                hci_packets.InitiatorFilterPolicy.USE_CONNECT_LIST, hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                hci_packets.AddressType.RANDOM_DEVICE_ADDRESS, 'BA:D5:A4:A3:A2:A1', 1, [phy_scan_params]))
+
+        # CERT Advertises
+        advertising_handle = 1
+        self.send_hal_hci_command(
+            hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                advertising_handle,
+                hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                512,
+                768,
+                7,
+                hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                'A6:A5:A4:A3:A2:A1',
+                hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                0x7F,
+                0,  # SID
+                hci_packets.Enable.DISABLED  # Scan request notification
+            ))
+
+        self.send_hal_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.send_hal_hci_command(
+            hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
+        enabled_set = hci_packets.EnabledSet()
+        enabled_set.advertising_handle = 1
+        enabled_set.duration = 0
+        enabled_set.max_extended_advertising_events = 0
+        self.send_hal_hci_command(
+            hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
+
+        # LeConnectionComplete
+        self._verify_le_connection_complete()
+
+    def _verify_connection_complete(self):
+        cert_connection_complete_capture = HalCaptures.ConnectionCompleteCapture()
+        assertThat(self.cert_hal.get_hci_event_stream()).emits(cert_connection_complete_capture)
+        cert_handle = cert_connection_complete_capture.get().GetConnectionHandle()
+
+        dut_connection_complete_capture = HciCaptures.ConnectionCompleteCapture()
+        assertThat(self.dut_hci.get_event_stream()).emits(dut_connection_complete_capture)
+        dut_handle = dut_connection_complete_capture.get().GetConnectionHandle()
+
+        return (dut_handle, cert_handle)
+
+    def test_connection_dut_connects(self):
+        self.dut_hci.send_command_with_complete(hci_packets.WritePageTimeoutBuilder(0x4000))
+
+        # CERT Enables scans and gets its address
+        self.send_hal_hci_command(hci_packets.ReadBdAddrBuilder())
+
+        cert_read_bd_addr_capture = HalCaptures.ReadBdAddrCompleteCapture()
+        assertThat(self.cert_hal.get_hci_event_stream()).emits(cert_read_bd_addr_capture)
+        address = cert_read_bd_addr_capture.get().GetBdAddr()
+
+        self.send_hal_hci_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+
+        # DUT Connects
+        self.dut_hci.send_command_with_status(
+            hci_packets.CreateConnectionBuilder(
+                address,
+                0xcc18,  # Packet Type
+                hci_packets.PageScanRepetitionMode.R0,
+                0,
+                hci_packets.ClockOffsetValid.INVALID,
+                hci_packets.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH))
+
+        # Cert Accepts
+        connect_request_capture = HalCaptures.ConnectionRequestCapture()
+        assertThat(self.cert_hal.get_hci_event_stream()).emits(connect_request_capture, timeout=timedelta(seconds=20))
+        connection_request = connect_request_capture.get()
+        self.send_hal_hci_command(
+            hci_packets.AcceptConnectionRequestBuilder(connection_request.GetBdAddr(),
+                                                       hci_packets.AcceptConnectionRequestRole.REMAIN_SLAVE))
+
+        (dut_handle, cert_handle) = self._verify_connection_complete()
+
+        # Send ACL Data
+        self.enqueue_acl_data(dut_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                              hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeAclData'))
+        self.send_hal_acl_data(cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                               hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(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.data)
+
+    def test_connection_cert_connects(self):
+        self.send_hal_hci_command(hci_packets.WritePageTimeoutBuilder(0x4000))
+
+        # DUT Enables scans and gets its address
+        self.dut_hci.send_command_with_complete(
+            hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+        self.dut_hci.send_command_with_complete(hci_packets.ReadBdAddrBuilder())
+
+        read_bd_addr_capture = HciCaptures.ReadBdAddrCompleteCapture()
+        assertThat(self.dut_hci.get_event_stream()).emits(read_bd_addr_capture)
+        address = read_bd_addr_capture.get().GetBdAddr()
+
+        # Cert Connects
+        self.send_hal_hci_command(
+            hci_packets.CreateConnectionBuilder(
+                address,
+                0xcc18,  # Packet Type
+                hci_packets.PageScanRepetitionMode.R0,
+                0,
+                hci_packets.ClockOffsetValid.INVALID,
+                hci_packets.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH))
+
+        # DUT Accepts
+        connection_request_capture = HciCaptures.ConnectionRequestCapture()
+        assertThat(self.dut_hci.get_event_stream()).emits(connection_request_capture, timeout=timedelta(seconds=20))
+        connection_request = connection_request_capture.get()
+        self.dut_hci.send_command_with_status(
+            hci_packets.AcceptConnectionRequestBuilder(connection_request.GetBdAddr(),
+                                                       hci_packets.AcceptConnectionRequestRole.REMAIN_SLAVE))
+
+        (dut_handle, cert_handle) = self._verify_connection_complete()
+
+        # Send ACL Data
+        self.enqueue_acl_data(dut_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                              hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'This is just SomeAclData'))
+        self.send_hal_acl_data(cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                               hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(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.data)
diff --git a/gd/hci/cert/le_acl_manager_test.py b/gd/hci/cert/le_acl_manager_test.py
new file mode 100644
index 0000000..6c2befb
--- /dev/null
+++ b/gd/hci/cert/le_acl_manager_test.py
@@ -0,0 +1,296 @@
+#!/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.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 facade_pb2 as hci_facade
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets
+
+
+class LeAclManagerTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(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.FetchLeSubevents(empty_proto.Empty()))
+        self.cert_acl_data_stream = EventStream(self.cert.hci.FetchAclPackets(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)
+        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.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 = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.cert.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert.hci.EnqueueCommandWithStatus(cmd)
+
+    def enqueue_acl_data(self, handle, pb_flag, b_flag, acl):
+        acl_msg = hci_facade.AclMsg(
+            handle=int(handle), packet_boundary_flag=int(pb_flag), broadcast_flag=int(b_flag), data=acl)
+        self.cert.hci.SendAclData(acl_msg)
+
+    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
+            ),
+            True)
+
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0C:05:04:03:02:01'), True)
+
+        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]), True)
+
+        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]), True)
+
+        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]), True)
+
+        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.event
+            nonlocal handle
+            nonlocal address
+            if b'\x3e\x13\x01\x00' in packet_bytes:
+                cc_view = hci_packets.LeConnectionCompleteView(
+                    hci_packets.LeMetaEventView(
+                        hci_packets.EventPacketView(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.EventPacketView(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.data)
+        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,
+            event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            address_type=common.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'), True)
+        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]), False)
+
+        # Cert gets ConnectionComplete with a handle and sends ACL data
+        handle = 0xfff
+
+        def get_handle(packet):
+            packet_bytes = packet.event
+            nonlocal handle
+            if b'\x3e\x13\x01\x00' in packet_bytes:
+                cc_view = hci_packets.LeConnectionCompleteView(
+                    hci_packets.LeMetaEventView(
+                        hci_packets.EventPacketView(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.EventPacketView(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
new file mode 100644
index 0000000..5afda1b
--- /dev/null
+++ b/gd/hci/cert/le_advertising_manager_test.py
@@ -0,0 +1,94 @@
+#!/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.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 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
+
+
+class LeAdvertisingManagerTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI')
+
+    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 = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.cert.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert.hci.EnqueueCommandWithStatus(cmd)
+
+    def test_le_ad_scan_dut_advertises(self):
+        self.register_for_le_event(hci_packets.SubeventCode.ADVERTISING_REPORT)
+        self.register_for_le_event(hci_packets.SubeventCode.EXTENDED_ADVERTISING_REPORT)
+        with EventStream(self.cert.hci.FetchLeSubevents(empty_proto.Empty())) as hci_le_event_stream:
+
+            # CERT Scans
+            self.enqueue_hci_command(hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'), True)
+            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.enqueue_hci_command(
+                hci_packets.LeSetExtendedScanParametersBuilder(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                                                               hci_packets.LeScanningFilterPolicy.ACCEPT_ALL, 1,
+                                                               [scan_parameters]), True)
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedScanEnableBuilder(hci_packets.Enable.ENABLED,
+                                                           hci_packets.FilterDuplicates.DISABLED, 0, 0), True)
+
+            # 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,
+                event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+                address_type=common.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)
+
+            hci_le_event_stream.assert_event_occurs(lambda packet: b'Im_The_DUT' in packet.event)
+
+            remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
+            self.dut.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
+            self.enqueue_hci_command(
+                hci_packets.LeSetScanEnableBuilder(hci_packets.Enable.DISABLED, hci_packets.Enable.DISABLED), True)
diff --git a/gd/hci/cert/le_scanning_manager_test.py b/gd/hci/cert/le_scanning_manager_test.py
new file mode 100644
index 0000000..4ea9b99
--- /dev/null
+++ b/gd/hci/cert/le_scanning_manager_test.py
@@ -0,0 +1,97 @@
+#!/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.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 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 LeScanningManagerTest(GdBaseTestClass):
+
+    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 = hci_facade.CommandMsg(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):
+        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,
+                event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+                address_type=common.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/simple_hci_test.py b/gd/hci/cert/simple_hci_test.py
deleted file mode 100644
index 522f643..0000000
--- a/gd/hci/cert/simple_hci_test.py
+++ /dev/null
@@ -1,344 +0,0 @@
-#!/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 __future__ import print_function
-
-import logging
-import os
-import sys
-sys.path.append(os.environ['ANDROID_BUILD_TOP'] + '/system/bt/gd')
-
-from cert.gd_base_test import GdBaseTestClass
-from cert.event_callback_stream import EventCallbackStream
-from cert.event_asserts import EventAsserts
-from cert import rootservice_pb2 as cert_rootservice_pb2
-from facade import common_pb2
-from facade import rootservice_pb2 as facade_rootservice_pb2
-from google.protobuf import empty_pb2
-from hci import facade_pb2 as hci_facade_pb2
-from hci.cert import api_pb2 as hci_cert_pb2
-from hci.cert import api_pb2_grpc as hci_cert_pb2_grpc
-
-class SimpleHciTest(GdBaseTestClass):
-
-    def setup_test(self):
-        self.device_under_test = self.gd_devices[0]
-        self.cert_device = self.gd_cert_devices[0]
-
-        self.device_under_test.rootservice.StartStack(
-            facade_rootservice_pb2.StartStackRequest(
-                module_under_test=facade_rootservice_pb2.BluetoothModule.Value('HCI'),
-            )
-        )
-        self.cert_device.rootservice.StartStack(
-            cert_rootservice_pb2.StartStackRequest(
-                module_to_test=cert_rootservice_pb2.BluetoothModule.Value('HCI'),
-            )
-        )
-
-        self.device_under_test.wait_channel_ready()
-        self.cert_device.wait_channel_ready()
-
-        self.device_under_test.hci.SetPageScanMode(
-            hci_facade_pb2.PageScanMode(enabled=True)
-        )
-        self.cert_device.hci.SetPageScanMode(
-            hci_cert_pb2.PageScanMode(enabled=True)
-        )
-
-        dut_address = self.device_under_test.controller_read_only_property.ReadLocalAddress(empty_pb2.Empty()).address
-        self.device_under_test.address = dut_address
-        cert_address = self.cert_device.controller_read_only_property.ReadLocalAddress(empty_pb2.Empty()).address
-        self.cert_device.address = cert_address
-
-        self.dut_address = common_pb2.BluetoothAddress(
-            address=self.device_under_test.address)
-        self.cert_address = common_pb2.BluetoothAddress(
-            address=self.cert_device.address)
-
-    def teardown_test(self):
-        self.device_under_test.rootservice.StopStack(
-            facade_rootservice_pb2.StopStackRequest()
-        )
-        self.cert_device.rootservice.StopStack(
-            cert_rootservice_pb2.StopStackRequest()
-        )
-
-    def test_none_event(self):
-        with EventCallbackStream(self.device_under_test.hci.FetchConnectionComplete(empty_pb2.Empty())) as connection_complete_stream:
-            connection_complete_asserts = EventAsserts(connection_complete_stream)
-            connection_complete_asserts.assert_none()
-
-    def _connect_from_dut(self):
-        logging.debug("_connect_from_dut")
-        policy = hci_cert_pb2.IncomingConnectionPolicy(
-            remote=self.dut_address,
-            accepted=True
-        )
-        self.cert_device.hci.SetIncomingConnectionPolicy(policy)
-
-        with EventCallbackStream(self.device_under_test.hci.FetchConnectionComplete(empty_pb2.Empty())) as dut_connection_complete_stream:
-            dut_connection_complete_asserts = EventAsserts(dut_connection_complete_stream)
-            self.device_under_test.hci.Connect(self.cert_address)
-            dut_connection_complete_asserts.assert_event_occurs(
-                lambda event: self._get_handle(event)
-            )
-
-    def _disconnect_from_dut(self):
-        logging.debug("_disconnect_from_dut")
-        with EventCallbackStream(self.device_under_test.hci.FetchDisconnection(empty_pb2.Empty())) as dut_disconnection_stream:
-            dut_disconnection_asserts = EventAsserts(dut_disconnection_stream)
-            self.device_under_test.hci.Disconnect(self.cert_address)
-            dut_disconnection_asserts.assert_event_occurs(
-                lambda event: event.remote.address == self.cert_device.address)
-
-    def _get_handle(self, event):
-        if event.remote.address == self.cert_device.address:
-            self.connection_handle = event.connection_handle
-            return True
-        return False
-
-    def test_connect_disconnect_send_acl(self):
-        self._connect_from_dut()
-
-        with EventCallbackStream(self.cert_device.hci.FetchAclData(empty_pb2.Empty())) as cert_acl_stream:
-            cert_acl_asserts = EventAsserts(cert_acl_stream)
-            acl_data = hci_facade_pb2.AclData(remote=self.cert_address, payload=b'123')
-            self.device_under_test.hci.SendAclData(acl_data)
-            self.device_under_test.hci.SendAclData(acl_data)
-            self.device_under_test.hci.SendAclData(acl_data)
-            cert_acl_asserts.assert_event_occurs(
-                lambda packet : b'123' in packet.payload
-                and packet.remote == self.dut_address
-            )
-
-        self._disconnect_from_dut()
-
-    def test_connect_disconnect_receive_acl(self):
-        self._connect_from_dut()
-
-        with EventCallbackStream(self.device_under_test.hci.FetchAclData(empty_pb2.Empty())) as dut_acl_stream:
-            dut_acl_asserts = EventAsserts(dut_acl_stream)
-            acl_data = hci_cert_pb2.AclData(remote=self.dut_address, payload=b'123')
-            self.cert_device.hci.SendAclData(acl_data)
-            self.cert_device.hci.SendAclData(acl_data)
-            self.cert_device.hci.SendAclData(acl_data)
-            dut_acl_asserts.assert_event_occurs(
-                lambda packet : b'123' in packet.payload
-                and packet.remote == self.cert_address
-            )
-
-        self._disconnect_from_dut()
-
-    def test_reject_connection_request(self):
-        with EventCallbackStream(self.device_under_test.hci.FetchConnectionFailed(empty_pb2.Empty())) as dut_connection_failed_stream:
-            dut_connection_failed_asserts = EventAsserts(dut_connection_failed_stream)
-            self.device_under_test.hci.Connect(self.cert_address)
-            dut_connection_failed_asserts.assert_event_occurs(
-                lambda event : event.remote == self.cert_address
-            )
-
-    def test_send_classic_security_command(self):
-        self._connect_from_dut()
-
-        with EventCallbackStream(self.device_under_test.hci_classic_security.FetchCommandCompleteEvent(empty_pb2.Empty())) as dut_command_complete_stream:
-            dut_command_complete_asserts = EventAsserts(dut_command_complete_stream)
-
-            self.device_under_test.hci.AuthenticationRequested(self.cert_address)
-
-            # Link request
-            self.device_under_test.hci_classic_security.LinkKeyRequestNegativeReply(self.cert_address)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x040c
-            )
-
-            # Pin code request
-            message = hci_facade_pb2.PinCodeRequestReplyMessage(
-                remote=self.cert_address,
-                len=4,
-                pin_code=bytes("1234", encoding = "ASCII")
-            )
-            self.device_under_test.hci_classic_security.PinCodeRequestReply(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x040d
-            )
-            self.device_under_test.hci_classic_security.PinCodeRequestNegativeReply(self.cert_address)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x040e
-            )
-
-            # IO capability request
-            message = hci_facade_pb2.IoCapabilityRequestReplyMessage(
-                remote=self.cert_address,
-                io_capability=0,
-                oob_present=0,
-                authentication_requirements=0
-            )
-            self.device_under_test.hci_classic_security.IoCapabilityRequestReply(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x042b
-            )
-
-            # message = hci_facade_pb2.IoCapabilityRequestNegativeReplyMessage(
-            #     remote=self.cert_address,
-            #     reason=1
-            # )
-            # # link_layer_controller.cc(447)] Check failed: security_manager_.GetAuthenticationAddress() == peer
-            # self.device_under_test.hci_classic_security.IoCapabilityRequestNegativeReply(message)
-
-            # User confirm request
-            self.device_under_test.hci_classic_security.UserConfirmationRequestReply(self.cert_address)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x042c
-            )
-
-            message = hci_facade_pb2.LinkKeyRequestReplyMessage(
-                remote=self.cert_address,
-                link_key=bytes("4C68384139F574D836BCF34E9DFB01BF", encoding = "ASCII")
-            )
-            self.device_under_test.hci_classic_security.LinkKeyRequestReply(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x040b
-            )
-
-            self.device_under_test.hci_classic_security.UserConfirmationRequestNegativeReply(self.cert_address)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x042d
-            )
-
-            # User passkey request
-            message = hci_facade_pb2.UserPasskeyRequestReplyMessage(
-                remote=self.cert_address,
-                passkey=999999,
-            )
-            self.device_under_test.hci_classic_security.UserPasskeyRequestReply(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x042e
-            )
-
-            self.device_under_test.hci_classic_security.UserPasskeyRequestNegativeReply(self.cert_address)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x042f
-            )
-
-            # Remote OOB data request
-            message = hci_facade_pb2.RemoteOobDataRequestReplyMessage(
-                remote=self.cert_address,
-                c=b'\x19\x20\x21\x22\x23\x24\x25\x26\x19\x20\x21\x22\x23\x24\x25\x26',
-                r=b'\x30\x31\x32\x33\x34\x35\x36\x37\x30\x31\x32\x33\x34\x35\x36\x37',
-            )
-            self.device_under_test.hci_classic_security.RemoteOobDataRequestReply(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0430
-            )
-            self.device_under_test.hci_classic_security.RemoteOobDataRequestNegativeReply(self.cert_address)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0433
-            )
-
-            # Read/Write/Delete link key
-            message = hci_facade_pb2.ReadStoredLinkKeyMessage(
-                remote=self.cert_address,
-                read_all_flag = 0,
-            )
-            self.device_under_test.hci_classic_security.ReadStoredLinkKey(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0c0d
-            )
-
-            message = hci_facade_pb2.WriteStoredLinkKeyMessage(
-                num_keys_to_write=1,
-                remote=self.cert_address,
-                link_keys=bytes("4C68384139F574D836BCF34E9DFB01BF", encoding = "ASCII"),
-            )
-            self.device_under_test.hci_classic_security.WriteStoredLinkKey(message)
-
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0c11
-            )
-
-            message = hci_facade_pb2.DeleteStoredLinkKeyMessage(
-                remote=self.cert_address,
-                delete_all_flag = 0,
-            )
-            self.device_under_test.hci_classic_security.DeleteStoredLinkKey(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0c12
-            )
-
-            # Refresh Encryption Key
-            message = hci_facade_pb2.RefreshEncryptionKeyMessage(
-                connection_handle=self.connection_handle,
-            )
-            self.device_under_test.hci_classic_security.RefreshEncryptionKey(message)
-
-            # Read/Write Simple Pairing Mode
-            self.device_under_test.hci_classic_security.ReadSimplePairingMode(empty_pb2.Empty())
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0c55
-            )
-
-            message = hci_facade_pb2.WriteSimplePairingModeMessage(
-                simple_pairing_mode=1,
-            )
-            self.device_under_test.hci_classic_security.WriteSimplePairingMode(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0c56
-            )
-
-            # Read local oob data
-            self.device_under_test.hci_classic_security.ReadLocalOobData(empty_pb2.Empty())
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0c57
-            )
-
-            # Send keypress notification
-            message = hci_facade_pb2.SendKeypressNotificationMessage(
-                remote=self.cert_address,
-                notification_type=1,
-            )
-            self.device_under_test.hci_classic_security.SendKeypressNotification(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0c60
-            )
-
-            # Read local oob extended data
-            self.device_under_test.hci_classic_security.ReadLocalOobExtendedData(empty_pb2.Empty())
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x0c7d
-            )
-
-            # Read Encryption key size
-            message = hci_facade_pb2.ReadEncryptionKeySizeMessage(
-                connection_handle=self.connection_handle,
-            )
-            self.device_under_test.hci_classic_security.ReadEncryptionKeySize(message)
-            dut_command_complete_asserts.assert_event_occurs(
-                lambda event: event.command_opcode == 0x1408
-            )
-
-        self._disconnect_from_dut()
-
-    def test_internal_hci_command(self):
-        self._connect_from_dut()
-        self.device_under_test.hci.TestInternalHciCommands(empty_pb2.Empty())
-        self.device_under_test.hci.TestInternalHciLeCommands(empty_pb2.Empty())
-        self._disconnect_from_dut()
-
-    def test_classic_connection_management_command(self):
-        self._connect_from_dut()
-        self.device_under_test.hci.TestClassicConnectionManagementCommands(self.cert_address)
-        self._disconnect_from_dut()
\ No newline at end of file
diff --git a/gd/hci/class_of_device.cc b/gd/hci/class_of_device.cc
index 36dd292..9c6817f 100644
--- a/gd/hci/class_of_device.cc
+++ b/gd/hci/class_of_device.cc
@@ -30,6 +30,8 @@
 
 static_assert(sizeof(ClassOfDevice) == ClassOfDevice::kLength, "ClassOfDevice must be 3 bytes long!");
 
+// ClassOfDevice cannot initialize member variables as it is a POD type
+// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
 ClassOfDevice::ClassOfDevice(const uint8_t (&class_of_device)[kLength]) {
   std::copy(class_of_device, class_of_device + kLength, cod);
 };
@@ -43,7 +45,7 @@
 }
 
 bool ClassOfDevice::FromString(const std::string& from, ClassOfDevice& to) {
-  ClassOfDevice new_cod;
+  ClassOfDevice new_cod{};
   if (from.length() != 8) return false;
 
   std::istringstream stream(from);
@@ -90,7 +92,7 @@
 };
 
 bool ClassOfDevice::IsValid(const std::string& cod) {
-  ClassOfDevice tmp;
+  ClassOfDevice tmp{};
   return ClassOfDevice::FromString(cod, tmp);
 }
 }  // namespace hci
diff --git a/gd/hci/class_of_device.h b/gd/hci/class_of_device.h
index b44a45e..9011df3 100644
--- a/gd/hci/class_of_device.h
+++ b/gd/hci/class_of_device.h
@@ -31,7 +31,6 @@
 
   ClassOfDevice() = default;
   ClassOfDevice(const uint8_t (&class_of_device)[kLength]);
-
   bool operator==(const ClassOfDevice& rhs) const {
     return (std::memcmp(cod, rhs.cod, sizeof(cod)) == 0);
   }
diff --git a/gd/hci/class_of_device_pybind11_type_caster.h b/gd/hci/class_of_device_pybind11_type_caster.h
new file mode 100644
index 0000000..0615e3b
--- /dev/null
+++ b/gd/hci/class_of_device_pybind11_type_caster.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <cstring>
+#include <string>
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+#include "hci/class_of_device.h"
+
+namespace py = pybind11;
+
+namespace pybind11 {
+namespace detail {
+template <>
+struct type_caster<::bluetooth::hci::ClassOfDevice> {
+ public:
+  // Set the Python name of ClassOfDevice and declare "value"
+  PYBIND11_TYPE_CASTER(::bluetooth::hci::ClassOfDevice, _("ClassOfDevice"));
+
+  // Convert from Python->C++
+  bool load(handle src, bool) {
+    PyObject* source = src.ptr();
+    if (py::isinstance<py::str>(src)) {
+      bool conversion_successful = bluetooth::hci::ClassOfDevice::FromString(PyUnicode_AsUTF8(source), value);
+      return conversion_successful && !PyErr_Occurred();
+    }
+    return false;
+  }
+
+  // Convert from C++->Python
+  static handle cast(bluetooth::hci::ClassOfDevice src, return_value_policy, handle) {
+    return PyUnicode_FromString(src.ToString().c_str());
+  }
+};
+}  // namespace detail
+}  // namespace pybind11
diff --git a/gd/hci/classic_security_manager.cc b/gd/hci/classic_security_manager.cc
deleted file mode 100644
index 07aae77..0000000
--- a/gd/hci/classic_security_manager.cc
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "classic_security_manager.h"
-
-#include <future>
-#include <set>
-#include <utility>
-#include "os/log.h"
-
-#include "acl_manager.h"
-#include "common/bidi_queue.h"
-#include "hci/controller.h"
-#include "hci/hci_layer.h"
-
-namespace bluetooth {
-namespace hci {
-
-using common::Bind;
-using common::BindOnce;
-
-struct ClassicSecurityManager::impl {
-  impl(ClassicSecurityManager& classic_security_manager) : classic_security_manager_(classic_security_manager) {}
-
-  void Start() {
-    hci_layer_ = classic_security_manager_.GetDependency<HciLayer>();
-    handler_ = classic_security_manager_.GetHandler();
-    hci_layer_->RegisterEventHandler(EventCode::IO_CAPABILITY_REQUEST,
-                                     Bind(&impl::on_request_event, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::LINK_KEY_REQUEST,
-                                     Bind(&impl::on_request_event, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::PIN_CODE_REQUEST,
-                                     Bind(&impl::on_request_event, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE,
-                                     Bind(&impl::on_complete_event, common::Unretained(this)), handler_);
-    hci_layer_->RegisterEventHandler(EventCode::LINK_KEY_NOTIFICATION,
-                                     Bind(&impl::on_link_key_notification, common::Unretained(this)), handler_);
-  }
-
-  void Stop() {
-    hci_layer_->UnregisterEventHandler(EventCode::IO_CAPABILITY_REQUEST);
-    handler_ = nullptr;
-    hci_layer_ = nullptr;
-  }
-
-  void handle_register_callbacks(ClassicSecurityCommandCallbacks* callbacks, os::Handler* handler) {
-    ASSERT(client_callbacks_ == nullptr);
-    ASSERT(client_handler_ == nullptr);
-    client_callbacks_ = callbacks;
-    client_handler_ = handler;
-  }
-
-  void link_key_request_reply(Address address, common::LinkKey link_key) {
-    std::array<uint8_t, 16> link_key_array;
-    std::copy(std::begin(link_key.link_key), std::end(link_key.link_key), std::begin(link_key_array));
-
-    std::unique_ptr<LinkKeyRequestReplyBuilder> packet = LinkKeyRequestReplyBuilder::Create(address, link_key_array);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void link_key_request_negative_reply(Address address) {
-    std::unique_ptr<LinkKeyRequestNegativeReplyBuilder> packet = LinkKeyRequestNegativeReplyBuilder::Create(address);
-
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void pin_code_request_reply(Address address, uint8_t len, std::string pin_code) {
-    ASSERT(len > 0 && len <= 16 && pin_code.length() == len);
-    // fill remaining char with 0
-    pin_code.append(std::string(16 - len, '0'));
-    std::array<uint8_t, 16> pin_code_array;
-    std::copy(std::begin(pin_code), std::end(pin_code), std::begin(pin_code_array));
-
-    std::unique_ptr<PinCodeRequestReplyBuilder> packet =
-        PinCodeRequestReplyBuilder::Create(address, len, pin_code_array);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void pin_code_request_negative_reply(Address address) {
-    std::unique_ptr<PinCodeRequestNegativeReplyBuilder> packet = PinCodeRequestNegativeReplyBuilder::Create(address);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void io_capability_request_reply(Address address, IoCapability io_capability, OobDataPresent oob_present,
-                                   AuthenticationRequirements authentication_requirements) {
-    std::unique_ptr<IoCapabilityRequestReplyBuilder> packet =
-        IoCapabilityRequestReplyBuilder::Create(address, io_capability, oob_present, authentication_requirements);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void io_capability_request_negative_reply(Address address, ErrorCode reason) {
-    std::unique_ptr<IoCapabilityRequestNegativeReplyBuilder> packet =
-        IoCapabilityRequestNegativeReplyBuilder::Create(address, reason);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void user_confirmation_request_reply(Address address) {
-    std::unique_ptr<UserConfirmationRequestReplyBuilder> packet = UserConfirmationRequestReplyBuilder::Create(address);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void user_confirmation_request_negative_reply(Address address) {
-    std::unique_ptr<UserConfirmationRequestNegativeReplyBuilder> packet =
-        UserConfirmationRequestNegativeReplyBuilder::Create(address);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void user_passkey_request_reply(Address address, uint32_t passkey) {
-    ASSERT(passkey <= 999999);
-    std::unique_ptr<UserPasskeyRequestReplyBuilder> packet = UserPasskeyRequestReplyBuilder::Create(address, passkey);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void user_passkey_request_negative_reply(Address address) {
-    std::unique_ptr<UserPasskeyRequestNegativeReplyBuilder> packet =
-        UserPasskeyRequestNegativeReplyBuilder::Create(address);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void remote_oob_data_request_reply(Address address, std::array<uint8_t, 16> c, std::array<uint8_t, 16> r) {
-    std::unique_ptr<RemoteOobDataRequestReplyBuilder> packet = RemoteOobDataRequestReplyBuilder::Create(address, c, r);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void remote_oob_data_request_negative_reply(Address address) {
-    std::unique_ptr<RemoteOobDataRequestNegativeReplyBuilder> packet =
-        RemoteOobDataRequestNegativeReplyBuilder::Create(address);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void read_stored_link_key(Address address, ReadStoredLinkKeyReadAllFlag read_all_flag) {
-    std::unique_ptr<ReadStoredLinkKeyBuilder> packet = ReadStoredLinkKeyBuilder::Create(address, read_all_flag);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void write_stored_link_key(std::vector<KeyAndAddress> keys) {
-    std::unique_ptr<WriteStoredLinkKeyBuilder> packet = WriteStoredLinkKeyBuilder::Create(keys);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void delete_stored_link_key(Address address, DeleteStoredLinkKeyDeleteAllFlag delete_all_flag) {
-    std::unique_ptr<DeleteStoredLinkKeyBuilder> packet = DeleteStoredLinkKeyBuilder::Create(address, delete_all_flag);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void refresh_encryption_key(uint16_t connection_handle) {
-    std::unique_ptr<RefreshEncryptionKeyBuilder> packet = RefreshEncryptionKeyBuilder::Create(connection_handle);
-    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandStatusView status) { /* TODO: check? */ }),
-                               handler_);
-  }
-
-  void read_simple_pairing_mode() {
-    std::unique_ptr<ReadSimplePairingModeBuilder> packet = ReadSimplePairingModeBuilder::Create();
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void write_simple_pairing_mode(Enable connection_handle) {
-    std::unique_ptr<WriteSimplePairingModeBuilder> packet = WriteSimplePairingModeBuilder::Create(connection_handle);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void read_local_oob_data() {
-    std::unique_ptr<ReadLocalOobDataBuilder> packet = ReadLocalOobDataBuilder::Create();
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void send_keypress_notification(Address address, KeypressNotificationType notification_type) {
-    std::unique_ptr<SendKeypressNotificationBuilder> packet =
-        SendKeypressNotificationBuilder::Create(address, notification_type);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void read_local_oob_extended_data() {
-    std::unique_ptr<ReadLocalOobExtendedDataBuilder> packet = ReadLocalOobExtendedDataBuilder::Create();
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  void read_encryption_key_size(uint16_t connection_handle) {
-    std::unique_ptr<ReadEncryptionKeySizeBuilder> packet = ReadEncryptionKeySizeBuilder::Create(connection_handle);
-    hci_layer_->EnqueueCommand(std::move(packet),
-                               common::BindOnce(&impl::on_command_complete, common::Unretained(this)), handler_);
-  }
-
-  // TODO remove
-  void on_request_event(EventPacketView packet) {
-    EventCode event_code = packet.GetEventCode();
-    LOG_DEBUG("receive request %d", (uint8_t)event_code);
-  }
-
-  // TODO remove
-  void on_complete_event(EventPacketView packet) {
-    EventCode event_code = packet.GetEventCode();
-    LOG_DEBUG("receive complete event %d", (uint8_t)event_code);
-  }
-
-  void on_link_key_notification(EventPacketView packet) {
-    auto view = LinkKeyNotificationView::Create(packet);
-    ASSERT(view.IsValid());
-    LOG_DEBUG("receive link key notification, key type %d", (uint8_t)view.GetKeyType());
-  }
-
-  void on_command_complete(CommandCompleteView status) {
-    if (client_handler_ != nullptr) {
-      client_handler_->Post(common::BindOnce(&ClassicSecurityCommandCallbacks::OnCommandComplete,
-                                             common::Unretained(client_callbacks_), status));
-    }
-  }
-
-  ClassicSecurityManager& classic_security_manager_;
-
-  Controller* controller_ = nullptr;
-
-  HciLayer* hci_layer_ = nullptr;
-  os::Handler* handler_ = nullptr;
-  ClassicSecurityCommandCallbacks* client_callbacks_ = nullptr;
-  os::Handler* client_handler_ = nullptr;
-};
-
-ClassicSecurityManager::ClassicSecurityManager() : pimpl_(std::make_unique<impl>(*this)) {}
-
-bool ClassicSecurityManager::RegisterCallbacks(ClassicSecurityCommandCallbacks* callbacks, os::Handler* handler) {
-  ASSERT(callbacks != nullptr && handler != nullptr);
-  GetHandler()->Post(common::BindOnce(&impl::handle_register_callbacks, common::Unretained(pimpl_.get()),
-                                      common::Unretained(callbacks), common::Unretained(handler)));
-  return true;
-}
-
-void ClassicSecurityManager::LinkKeyRequestReply(Address address, common::LinkKey link_key) {
-  GetHandler()->Post(BindOnce(&impl::link_key_request_reply, common::Unretained(pimpl_.get()), address, link_key));
-}
-
-void ClassicSecurityManager::LinkKeyRequestNegativeReply(Address address) {
-  GetHandler()->Post(BindOnce(&impl::link_key_request_negative_reply, common::Unretained(pimpl_.get()), address));
-}
-
-void ClassicSecurityManager::PinCodeRequestReply(Address address, uint8_t len, std::string pin_code) {
-  GetHandler()->Post(BindOnce(&impl::pin_code_request_reply, common::Unretained(pimpl_.get()), address, len, pin_code));
-}
-
-void ClassicSecurityManager::PinCodeRequestNegativeReply(Address address) {
-  GetHandler()->Post(BindOnce(&impl::pin_code_request_negative_reply, common::Unretained(pimpl_.get()), address));
-}
-
-void ClassicSecurityManager::IoCapabilityRequestReply(Address address, IoCapability io_capability,
-                                                      OobDataPresent oob_present,
-                                                      AuthenticationRequirements authentication_requirements) {
-  GetHandler()->Post(BindOnce(&impl::io_capability_request_reply, common::Unretained(pimpl_.get()), address,
-                              io_capability, oob_present, authentication_requirements));
-}
-
-void ClassicSecurityManager::IoCapabilityRequestNegativeReply(Address address, ErrorCode reason) {
-  GetHandler()->Post(
-      BindOnce(&impl::io_capability_request_negative_reply, common::Unretained(pimpl_.get()), address, reason));
-}
-
-void ClassicSecurityManager::UserConfirmationRequestReply(Address address) {
-  GetHandler()->Post(BindOnce(&impl::user_confirmation_request_reply, common::Unretained(pimpl_.get()), address));
-}
-
-void ClassicSecurityManager::UserConfirmationRequestNegativeReply(Address address) {
-  GetHandler()->Post(
-      BindOnce(&impl::user_confirmation_request_negative_reply, common::Unretained(pimpl_.get()), address));
-}
-
-void ClassicSecurityManager::UserPasskeyRequestReply(bluetooth::hci::Address address, uint32_t passkey) {
-  GetHandler()->Post(BindOnce(&impl::user_passkey_request_reply, common::Unretained(pimpl_.get()), address, passkey));
-}
-
-void ClassicSecurityManager::UserPasskeyRequestNegativeReply(Address address) {
-  GetHandler()->Post(BindOnce(&impl::user_passkey_request_negative_reply, common::Unretained(pimpl_.get()), address));
-}
-
-void ClassicSecurityManager::RemoteOobDataRequestReply(Address address, std::array<uint8_t, 16> c,
-                                                       std::array<uint8_t, 16> r) {
-  GetHandler()->Post(BindOnce(&impl::remote_oob_data_request_reply, common::Unretained(pimpl_.get()), address, c, r));
-}
-
-void ClassicSecurityManager::RemoteOobDataRequestNegativeReply(Address address) {
-  GetHandler()->Post(
-      BindOnce(&impl::remote_oob_data_request_negative_reply, common::Unretained(pimpl_.get()), address));
-}
-
-void ClassicSecurityManager::ReadStoredLinkKey(Address address, ReadStoredLinkKeyReadAllFlag read_all_flag) {
-  GetHandler()->Post(BindOnce(&impl::read_stored_link_key, common::Unretained(pimpl_.get()), address, read_all_flag));
-}
-
-void ClassicSecurityManager::WriteStoredLinkKey(std::vector<KeyAndAddress> keys) {
-  GetHandler()->Post(BindOnce(&impl::write_stored_link_key, common::Unretained(pimpl_.get()), keys));
-}
-
-void ClassicSecurityManager::DeleteStoredLinkKey(Address address, DeleteStoredLinkKeyDeleteAllFlag delete_all_flag) {
-  GetHandler()->Post(
-      BindOnce(&impl::delete_stored_link_key, common::Unretained(pimpl_.get()), address, delete_all_flag));
-}
-
-void ClassicSecurityManager::RefreshEncryptionKey(uint16_t connection_handle) {
-  GetHandler()->Post(BindOnce(&impl::refresh_encryption_key, common::Unretained(pimpl_.get()), connection_handle));
-}
-void ClassicSecurityManager::ReadSimplePairingMode() {
-  GetHandler()->Post(BindOnce(&impl::read_simple_pairing_mode, common::Unretained(pimpl_.get())));
-}
-
-void ClassicSecurityManager::WriteSimplePairingMode(Enable simple_pairing_mode) {
-  GetHandler()->Post(BindOnce(&impl::write_simple_pairing_mode, common::Unretained(pimpl_.get()), simple_pairing_mode));
-}
-
-void ClassicSecurityManager::ReadLocalOobData() {
-  GetHandler()->Post(BindOnce(&impl::read_local_oob_data, common::Unretained(pimpl_.get())));
-}
-
-void ClassicSecurityManager::SendKeypressNotification(Address address, KeypressNotificationType notification_type) {
-  GetHandler()->Post(
-      BindOnce(&impl::send_keypress_notification, common::Unretained(pimpl_.get()), address, notification_type));
-}
-
-void ClassicSecurityManager::ReadLocalOobExtendedData() {
-  GetHandler()->Post(BindOnce(&impl::read_local_oob_extended_data, common::Unretained(pimpl_.get())));
-}
-
-void ClassicSecurityManager::ReadEncryptionKeySize(uint16_t connection_handle) {
-  GetHandler()->Post(BindOnce(&impl::read_encryption_key_size, common::Unretained(pimpl_.get()), connection_handle));
-}
-
-void ClassicSecurityManager::ListDependencies(ModuleList* list) {
-  list->add<HciLayer>();
-}
-
-void ClassicSecurityManager::Start() {
-  pimpl_->Start();
-}
-
-void ClassicSecurityManager::Stop() {
-  pimpl_->Stop();
-}
-
-std::string ClassicSecurityManager::ToString() const {
-  return "Classic Security Manager";
-}
-
-const ModuleFactory ClassicSecurityManager::Factory = ModuleFactory([]() { return new ClassicSecurityManager(); });
-
-}  // namespace hci
-}  // namespace bluetooth
diff --git a/gd/hci/classic_security_manager.h b/gd/hci/classic_security_manager.h
deleted file mode 100644
index 22b4ae1..0000000
--- a/gd/hci/classic_security_manager.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "common/link_key.h"
-#include "hci/address.h"
-#include "hci/hci_packets.h"
-#include "module.h"
-
-namespace bluetooth {
-namespace hci {
-
-class ClassicSecurityCommandCallbacks {
- public:
-  virtual ~ClassicSecurityCommandCallbacks() = default;
-  // Invoked when controller sends Command Complete event
-  virtual void OnCommandComplete(CommandCompleteView status) = 0;
-};
-
-class ClassicSecurityManager : public Module {
- public:
-  ClassicSecurityManager();
-
-  bool RegisterCallbacks(ClassicSecurityCommandCallbacks* callbacks, os::Handler* handler);
-
-  void LinkKeyRequestReply(Address address, common::LinkKey link_key);
-  void LinkKeyRequestNegativeReply(Address address);
-  void PinCodeRequestReply(Address address, uint8_t len, std::string pin_code);
-  void PinCodeRequestNegativeReply(Address address);
-  void IoCapabilityRequestReply(Address address, IoCapability io_capability, OobDataPresent oob_present,
-                                AuthenticationRequirements authentication_requirements);
-  void IoCapabilityRequestNegativeReply(Address address, ErrorCode reason);
-  void UserConfirmationRequestReply(Address address);
-  void UserConfirmationRequestNegativeReply(Address address);
-  void UserPasskeyRequestReply(Address address, uint32_t passkey);
-  void UserPasskeyRequestNegativeReply(Address address);
-  void RemoteOobDataRequestReply(Address address, std::array<uint8_t, 16> c, std::array<uint8_t, 16> r);
-  void RemoteOobDataRequestNegativeReply(Address address);
-  void ReadStoredLinkKey(Address address, ReadStoredLinkKeyReadAllFlag read_all_flag);
-  void WriteStoredLinkKey(std::vector<KeyAndAddress> keys);
-  void DeleteStoredLinkKey(Address address, DeleteStoredLinkKeyDeleteAllFlag delete_all_flag);
-  void RefreshEncryptionKey(uint16_t connection_handle);
-  void ReadSimplePairingMode();
-  void WriteSimplePairingMode(Enable simple_pairing_mode);
-  void ReadLocalOobData();
-  void SendKeypressNotification(Address address, KeypressNotificationType notification_type);
-  void ReadLocalOobExtendedData();
-  void ReadEncryptionKeySize(uint16_t connection_handle);
-
-  static const ModuleFactory Factory;
-
- protected:
-  void ListDependencies(ModuleList* list) override;
-
-  void Start() override;
-
-  void Stop() override;
-
-  std::string ToString() const override;
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-};
-
-}  // namespace hci
-}  // namespace bluetooth
diff --git a/gd/hci/classic_security_manager_test.cc b/gd/hci/classic_security_manager_test.cc
deleted file mode 100644
index b2c936e..0000000
--- a/gd/hci/classic_security_manager_test.cc
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "hci/classic_security_manager.h"
-
-#include <condition_variable>
-#include "gtest/gtest.h"
-
-#include "common/bind.h"
-#include "hci/hci_layer.h"
-#include "os/thread.h"
-#include "packet/raw_builder.h"
-
-namespace bluetooth {
-namespace hci {
-namespace {
-
-using common::BidiQueue;
-using common::BidiQueueEnd;
-using common::OnceCallback;
-using os::Handler;
-using os::Thread;
-using packet::RawBuilder;
-
-PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
-  auto bytes = std::make_shared<std::vector<uint8_t>>();
-  BitInserter i(*bytes);
-  bytes->reserve(packet->size());
-  packet->Serialize(i);
-  return packet::PacketView<packet::kLittleEndian>(bytes);
-}
-
-class CommandQueueEntry {
- public:
-  CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
-                    OnceCallback<void(CommandCompleteView)> on_complete_function, Handler* handler)
-      : command(std::move(command_packet)), waiting_for_status_(false), on_complete(std::move(on_complete_function)),
-        caller_handler(handler) {}
-
-  CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
-                    OnceCallback<void(CommandStatusView)> on_status_function, Handler* handler)
-      : command(std::move(command_packet)), waiting_for_status_(true), on_status(std::move(on_status_function)),
-        caller_handler(handler) {}
-
-  std::unique_ptr<CommandPacketBuilder> command;
-  bool waiting_for_status_;
-  OnceCallback<void(CommandStatusView)> on_status;
-  OnceCallback<void(CommandCompleteView)> on_complete;
-  Handler* caller_handler;
-};
-
-class TestHciLayer : public HciLayer {
- public:
-  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status,
-                      Handler* handler) override {
-    auto command_queue_entry = std::make_unique<CommandQueueEntry>(std::move(command), std::move(on_status), handler);
-    command_queue_.push(std::move(command_queue_entry));
-  }
-
-  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      OnceCallback<void(CommandCompleteView)> on_complete, Handler* handler) override {
-    auto command_queue_entry = std::make_unique<CommandQueueEntry>(std::move(command), std::move(on_complete), handler);
-    command_queue_.push(std::move(command_queue_entry));
-  }
-
-  std::unique_ptr<CommandQueueEntry> GetLastCommand() {
-    EXPECT_FALSE(command_queue_.empty());
-    auto last = std::move(command_queue_.front());
-    command_queue_.pop();
-    return last;
-  }
-
-  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
-                            Handler* handler) override {
-    registered_events_[event_code] = event_handler;
-  }
-
-  void UnregisterEventHandler(EventCode event_code) override {
-    registered_events_.erase(event_code);
-  }
-
-  void IncomingEvent(std::unique_ptr<EventPacketBuilder> event_builder) {
-    auto packet = GetPacketView(std::move(event_builder));
-    EventPacketView event = EventPacketView::Create(packet);
-    EXPECT_TRUE(event.IsValid());
-    EventCode event_code = event.GetEventCode();
-    EXPECT_TRUE(registered_events_.find(event_code) != registered_events_.end());
-    registered_events_[event_code].Run(event);
-  }
-
-  void ListDependencies(ModuleList* list) override {}
-  void Start() override {}
-  void Stop() override {}
-
- private:
-  std::map<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
-  std::queue<std::unique_ptr<CommandQueueEntry>> command_queue_;
-};
-
-class ClassicSecurityManagerTest : public ::testing::Test, public ::bluetooth::hci::ClassicSecurityCommandCallbacks {
- protected:
-  void SetUp() override {
-    test_hci_layer_ = new TestHciLayer;
-    handler_ = new Handler(&thread_);
-    fake_registry_.InjectTestModule(&TestHciLayer::Factory, test_hci_layer_);
-    fake_registry_.Start<ClassicSecurityManager>(&thread_);
-    classic_security_manager_ =
-        static_cast<ClassicSecurityManager*>(fake_registry_.GetModuleUnderTest(&ClassicSecurityManager::Factory));
-    classic_security_manager_->RegisterCallbacks(this, handler_);
-    test_hci_layer_->RegisterEventHandler(
-        EventCode::COMMAND_COMPLETE, base::Bind(&ClassicSecurityManagerTest::ExpectCommand, common::Unretained(this)),
-        nullptr);
-    test_hci_layer_->RegisterEventHandler(
-        EventCode::COMMAND_STATUS,
-        base::Bind(&ClassicSecurityManagerTest::ExpectCommandStatus, common::Unretained(this)), nullptr);
-
-    Address::FromString("A1:A2:A3:A4:A5:A6", remote);
-  }
-
-  void TearDown() override {
-    handler_->Clear();
-    delete handler_;
-    fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20));
-    fake_registry_.StopAll();
-    command_complete_ = false;
-  }
-
-  void ExpectCommand(EventPacketView packet) {
-    CommandCompleteView command_complete_view = CommandCompleteView::Create(std::move(packet));
-    auto last_command_queue_entry = test_hci_layer_->GetLastCommand();
-    auto last_command = std::move(last_command_queue_entry->command);
-    auto command_packet = GetPacketView(std::move(last_command));
-    CommandPacketView command_packet_view = CommandPacketView::Create(command_packet);
-
-    // verify command complete event match last command opcode
-    EXPECT_TRUE(command_packet_view.IsValid());
-    EXPECT_TRUE(command_complete_view.IsValid());
-    EXPECT_EQ(command_packet_view.GetOpCode(), command_complete_view.GetCommandOpCode());
-
-    // verify callback triggered
-    auto caller_handler = last_command_queue_entry->caller_handler;
-    caller_handler->Post(BindOnce(std::move(last_command_queue_entry->on_complete), std::move(command_complete_view)));
-    std::unique_lock<std::mutex> lock(mutex_);
-    EXPECT_FALSE(callback_done.wait_for(lock, std::chrono::seconds(3)) == std::cv_status::timeout);
-
-    command_complete_ = true;
-  }
-
-  void ExpectCommandStatus(EventPacketView packet) {
-    CommandStatusView command_status_view = CommandStatusView::Create(std::move(packet));
-    auto last_command_queue_entry = test_hci_layer_->GetLastCommand();
-    auto last_command = std::move(last_command_queue_entry->command);
-    auto command_packet = GetPacketView(std::move(last_command));
-    CommandPacketView command_packet_view = CommandPacketView::Create(command_packet);
-
-    // verify command complete event match last command opcode
-    EXPECT_TRUE(command_packet_view.IsValid());
-    EXPECT_TRUE(command_status_view.IsValid());
-    EXPECT_EQ(command_packet_view.GetOpCode(), command_status_view.GetCommandOpCode());
-
-    command_complete_ = true;
-  }
-
-  void OnCommandComplete(CommandCompleteView status) override {
-    callback_done.notify_one();
-  }
-
-  TestModuleRegistry fake_registry_;
-  TestHciLayer* test_hci_layer_ = nullptr;
-  os::Thread& thread_ = fake_registry_.GetTestThread();
-  Handler* handler_ = nullptr;
-  ClassicSecurityManager* classic_security_manager_ = nullptr;
-  Address remote;
-  mutable std::mutex mutex_;
-  std::condition_variable callback_done;
-  bool command_complete_ = false;
-};
-
-TEST_F(ClassicSecurityManagerTest, startup_teardown) {}
-
-TEST_F(ClassicSecurityManagerTest, send_link_key_request_reply) {
-  common::LinkKey link_key;
-  common::LinkKey::FromString("4c68384139f574d836bcf34e9dfb01bf\0", link_key);
-  classic_security_manager_->LinkKeyRequestReply(remote, link_key);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::LINK_KEY_REQUEST_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_link_key_request_negative_reply) {
-  classic_security_manager_->LinkKeyRequestNegativeReply(remote);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_pin_code_request_reply) {
-  classic_security_manager_->PinCodeRequestReply(remote, 6, "123456");
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::PIN_CODE_REQUEST_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_pin_code_request_negative_reply) {
-  classic_security_manager_->PinCodeRequestNegativeReply(remote);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::PIN_CODE_REQUEST_NEGATIVE_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_io_capability_request_reply) {
-  IoCapability io_capability = (IoCapability)0x00;
-  OobDataPresent oob_present = (OobDataPresent)0x00;
-  AuthenticationRequirements authentication_requirements = (AuthenticationRequirements)0x00;
-  classic_security_manager_->IoCapabilityRequestReply(remote, io_capability, oob_present, authentication_requirements);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::IO_CAPABILITY_REQUEST_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_io_capability_request_negative_reply) {
-  ErrorCode reason = (ErrorCode)0x01;
-  classic_security_manager_->IoCapabilityRequestNegativeReply(remote, reason);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::IO_CAPABILITY_REQUEST_NEGATIVE_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_user_confirmation_request_reply) {
-  classic_security_manager_->UserConfirmationRequestReply(remote);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::USER_CONFIRMATION_REQUEST_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_user_confirmation_request_negative_reply) {
-  classic_security_manager_->UserConfirmationRequestNegativeReply(remote);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_user_passkey_request_reply) {
-  classic_security_manager_->UserPasskeyRequestReply(remote, 999999);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::USER_PASSKEY_REQUEST_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_user_passkey_request_negative_reply) {
-  classic_security_manager_->UserPasskeyRequestNegativeReply(remote);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::USER_PASSKEY_REQUEST_NEGATIVE_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_remote_oob_data_request_reply) {
-  std::array<uint8_t, 16> c;
-  std::array<uint8_t, 16> r;
-  for (int i = 0; i < 16; i++) {
-    c[i] = (uint8_t)i;
-    r[i] = (uint8_t)i + 16;
-  }
-  classic_security_manager_->RemoteOobDataRequestReply(remote, c, r);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::REMOTE_OOB_DATA_REQUEST_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_remote_oob_data_request_negative_reply) {
-  classic_security_manager_->RemoteOobDataRequestNegativeReply(remote);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_read_stored_link_key) {
-  ReadStoredLinkKeyReadAllFlag read_all_flag = (ReadStoredLinkKeyReadAllFlag)0x01;
-  classic_security_manager_->ReadStoredLinkKey(remote, read_all_flag);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::READ_STORED_LINK_KEY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_delete_stored_link_key) {
-  DeleteStoredLinkKeyDeleteAllFlag delete_all_flag = (DeleteStoredLinkKeyDeleteAllFlag)0x01;
-  classic_security_manager_->DeleteStoredLinkKey(remote, delete_all_flag);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::DELETE_STORED_LINK_KEY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_refresh_encryption_key) {
-  classic_security_manager_->RefreshEncryptionKey(0x01);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandStatusBuilder::Create(ErrorCode::SUCCESS, 0x01, OpCode::REFRESH_ENCRYPTION_KEY, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_read_simple_pairing_mode) {
-  classic_security_manager_->ReadSimplePairingMode();
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::READ_SIMPLE_PAIRING_MODE, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_write_simple_pairing_mode) {
-  Enable simple_pairing_mode = (Enable)0x01;
-  classic_security_manager_->WriteSimplePairingMode(simple_pairing_mode);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::WRITE_SIMPLE_PAIRING_MODE, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_read_local_oob_data) {
-  classic_security_manager_->ReadLocalOobData();
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(CommandCompleteBuilder::Create(0x01, OpCode::READ_LOCAL_OOB_DATA, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_keypress_notification) {
-  KeypressNotificationType notification_type = (KeypressNotificationType)0x01;
-  classic_security_manager_->SendKeypressNotification(remote, notification_type);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::SEND_KEYPRESS_NOTIFICATION, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_read_local_oob_extended_data) {
-  classic_security_manager_->ReadLocalOobExtendedData();
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::READ_LOCAL_OOB_EXTENDED_DATA, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-TEST_F(ClassicSecurityManagerTest, send_read_encryption_key_size) {
-  classic_security_manager_->ReadEncryptionKeySize(0x01);
-  EXPECT_TRUE(fake_registry_.SynchronizeModuleHandler(&ClassicSecurityManager::Factory, std::chrono::milliseconds(20)));
-
-  auto payload = std::make_unique<RawBuilder>();
-  test_hci_layer_->IncomingEvent(
-      CommandCompleteBuilder::Create(0x01, OpCode::READ_ENCRYPTION_KEY_SIZE, std::move(payload)));
-  EXPECT_TRUE(command_complete_);
-}
-
-}  // namespace
-}  // namespace hci
-}  // namespace bluetooth
diff --git a/gd/hci/command_interface.h b/gd/hci/command_interface.h
new file mode 100644
index 0000000..eba01db
--- /dev/null
+++ b/gd/hci/command_interface.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "common/contextual_callback.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+template <typename T>
+class CommandInterface {
+ public:
+  CommandInterface() = default;
+  virtual ~CommandInterface() = default;
+  DISALLOW_COPY_AND_ASSIGN(CommandInterface);
+
+  virtual void EnqueueCommand(std::unique_ptr<T> command,
+                              common::ContextualOnceCallback<void(CommandCompleteView)> on_complete) = 0;
+
+  virtual void EnqueueCommand(std::unique_ptr<T> command,
+                              common::ContextualOnceCallback<void(CommandStatusView)> on_status) = 0;
+};
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/controller.cc b/gd/hci/controller.cc
index f6c53e5..e66ffb4 100644
--- a/gd/hci/controller.cc
+++ b/gd/hci/controller.cc
@@ -20,19 +20,11 @@
 #include <memory>
 #include <utility>
 
-#include "common/bind.h"
-#include "common/callback.h"
 #include "hci/hci_layer.h"
 
 namespace bluetooth {
 namespace hci {
 
-using common::Bind;
-using common::BindOnce;
-using common::Callback;
-using common::Closure;
-using common::OnceCallback;
-using common::OnceClosure;
 using os::Handler;
 
 struct Controller::impl {
@@ -40,81 +32,84 @@
 
   void Start(hci::HciLayer* hci) {
     hci_ = hci;
+    Handler* handler = module_.GetHandler();
     hci_->RegisterEventHandler(EventCode::NUMBER_OF_COMPLETED_PACKETS,
-                               Bind(&Controller::impl::NumberOfCompletedPackets, common::Unretained(this)),
-                               module_.GetHandler());
+                               handler->BindOn(this, &Controller::impl::NumberOfCompletedPackets));
 
+    set_event_mask(kDefaultEventMask);
+    write_simple_pairing_mode(Enable::ENABLED);
+    // TODO(b/159927452): Legacy stack set SimultaneousLeHost = 1. Revisit if this causes problem.
+    write_le_host_support(Enable::ENABLED, SimultaneousLeHost::DISABLED);
     hci_->EnqueueCommand(ReadLocalNameBuilder::Create(),
-                         BindOnce(&Controller::impl::read_local_name_complete_handler, common::Unretained(this)),
-                         module_.GetHandler());
-    hci_->EnqueueCommand(
-        ReadLocalVersionInformationBuilder::Create(),
-        BindOnce(&Controller::impl::read_local_version_information_complete_handler, common::Unretained(this)),
-        module_.GetHandler());
-    hci_->EnqueueCommand(
-        ReadLocalSupportedCommandsBuilder::Create(),
-        BindOnce(&Controller::impl::read_local_supported_commands_complete_handler, common::Unretained(this)),
-        module_.GetHandler());
-    hci_->EnqueueCommand(
-        ReadLocalSupportedFeaturesBuilder::Create(),
-        BindOnce(&Controller::impl::read_local_supported_features_complete_handler, common::Unretained(this)),
-        module_.GetHandler());
+                         handler->BindOnceOn(this, &Controller::impl::read_local_name_complete_handler));
+    hci_->EnqueueCommand(ReadLocalVersionInformationBuilder::Create(),
+                         handler->BindOnceOn(this, &Controller::impl::read_local_version_information_complete_handler));
+    hci_->EnqueueCommand(ReadLocalSupportedCommandsBuilder::Create(),
+                         handler->BindOnceOn(this, &Controller::impl::read_local_supported_commands_complete_handler));
+    hci_->EnqueueCommand(ReadLocalSupportedFeaturesBuilder::Create(),
+                         handler->BindOnceOn(this, &Controller::impl::read_local_supported_features_complete_handler));
 
     // Wait for all extended features read
     std::promise<void> features_promise;
     auto features_future = features_promise.get_future();
     hci_->EnqueueCommand(ReadLocalExtendedFeaturesBuilder::Create(0x00),
-                         BindOnce(&Controller::impl::read_local_extended_features_complete_handler,
-                                  common::Unretained(this), std::move(features_promise)),
-                         module_.GetHandler());
+                         handler->BindOnceOn(this, &Controller::impl::read_local_extended_features_complete_handler,
+                                             std::move(features_promise)));
     features_future.wait();
 
     hci_->EnqueueCommand(ReadBufferSizeBuilder::Create(),
-                         BindOnce(&Controller::impl::read_buffer_size_complete_handler, common::Unretained(this)),
-                         module_.GetHandler());
+                         handler->BindOnceOn(this, &Controller::impl::read_buffer_size_complete_handler));
 
-    hci_->EnqueueCommand(LeReadBufferSizeBuilder::Create(),
-                         BindOnce(&Controller::impl::le_read_buffer_size_handler, common::Unretained(this)),
-                         module_.GetHandler());
+    hci_->EnqueueCommand(LeReadBufferSizeV1Builder::Create(),
+                         handler->BindOnceOn(this, &Controller::impl::le_read_buffer_size_handler));
 
-    hci_->EnqueueCommand(
-        LeReadLocalSupportedFeaturesBuilder::Create(),
-        BindOnce(&Controller::impl::le_read_local_supported_features_handler, common::Unretained(this)),
-        module_.GetHandler());
+    hci_->EnqueueCommand(LeReadLocalSupportedFeaturesBuilder::Create(),
+                         handler->BindOnceOn(this, &Controller::impl::le_read_local_supported_features_handler));
 
     hci_->EnqueueCommand(LeReadSupportedStatesBuilder::Create(),
-                         BindOnce(&Controller::impl::le_read_supported_states_handler, common::Unretained(this)),
-                         module_.GetHandler());
+                         handler->BindOnceOn(this, &Controller::impl::le_read_supported_states_handler));
+
+    hci_->EnqueueCommand(
+        LeReadConnectListSizeBuilder::Create(),
+        handler->BindOnceOn(this, &Controller::impl::le_read_connect_list_size_handler));
+
+    hci_->EnqueueCommand(
+        LeReadResolvingListSizeBuilder::Create(),
+        handler->BindOnceOn(this, &Controller::impl::le_read_resolving_list_size_handler));
 
     if (is_supported(OpCode::LE_READ_MAXIMUM_DATA_LENGTH)) {
       hci_->EnqueueCommand(LeReadMaximumDataLengthBuilder::Create(),
-                           BindOnce(&Controller::impl::le_read_maximum_data_length_handler, common::Unretained(this)),
-                           module_.GetHandler());
+                           handler->BindOnceOn(this, &Controller::impl::le_read_maximum_data_length_handler));
+    } else {
+      le_maximum_data_length_.supported_max_rx_octets_ = 0;
+      le_maximum_data_length_.supported_max_rx_time_ = 0;
+      le_maximum_data_length_.supported_max_tx_octets_ = 0;
+      le_maximum_data_length_.supported_max_tx_time_ = 0;
     }
     if (is_supported(OpCode::LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH)) {
       hci_->EnqueueCommand(
           LeReadMaximumAdvertisingDataLengthBuilder::Create(),
-          BindOnce(&Controller::impl::le_read_maximum_advertising_data_length_handler, common::Unretained(this)),
-          module_.GetHandler());
+          handler->BindOnceOn(this, &Controller::impl::le_read_maximum_advertising_data_length_handler));
+    } else {
+      le_maximum_advertising_data_length_ = 31;
     }
     if (is_supported(OpCode::LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS)) {
       hci_->EnqueueCommand(
           LeReadNumberOfSupportedAdvertisingSetsBuilder::Create(),
-          BindOnce(&Controller::impl::le_read_number_of_supported_advertising_sets_handler, common::Unretained(this)),
-          module_.GetHandler());
+          handler->BindOnceOn(this, &Controller::impl::le_read_number_of_supported_advertising_sets_handler));
+    } else {
+      le_number_supported_advertising_sets_ = 1;
     }
 
     hci_->EnqueueCommand(LeGetVendorCapabilitiesBuilder::Create(),
-                         BindOnce(&Controller::impl::le_get_vendor_capabilities_handler, common::Unretained(this)),
-                         module_.GetHandler());
+                         handler->BindOnceOn(this, &Controller::impl::le_get_vendor_capabilities_handler));
 
     // We only need to synchronize the last read. Make BD_ADDR to be the last one.
     std::promise<void> promise;
     auto future = promise.get_future();
     hci_->EnqueueCommand(
         ReadBdAddrBuilder::Create(),
-        BindOnce(&Controller::impl::read_controller_mac_address_handler, common::Unretained(this), std::move(promise)),
-        module_.GetHandler());
+        handler->BindOnceOn(this, &Controller::impl::read_controller_mac_address_handler, std::move(promise)));
     future.wait();
   }
 
@@ -124,21 +119,27 @@
   }
 
   void NumberOfCompletedPackets(EventPacketView event) {
-    ASSERT(acl_credits_handler_ != nullptr);
+    if (acl_credits_callback_.IsEmpty()) {
+      LOG_WARN("Received event when AclManager is not listening");
+      return;
+    }
     auto complete_view = NumberOfCompletedPacketsView::Create(event);
     ASSERT(complete_view.IsValid());
     for (auto completed_packets : complete_view.GetCompletedPackets()) {
       uint16_t handle = completed_packets.connection_handle_;
       uint16_t credits = completed_packets.host_num_of_completed_packets_;
-      acl_credits_handler_->Post(Bind(acl_credits_callback_, handle, credits));
+      acl_credits_callback_.Invoke(handle, credits);
     }
   }
 
-  void RegisterCompletedAclPacketsCallback(Callback<void(uint16_t /* handle */, uint16_t /* packets */)> cb,
-                                           Handler* handler) {
-    ASSERT(acl_credits_handler_ == nullptr);
-    acl_credits_callback_ = cb;
-    acl_credits_handler_ = handler;
+  void register_completed_acl_packets_callback(CompletedAclPacketsCallback callback) {
+    ASSERT(acl_credits_callback_.IsEmpty());
+    acl_credits_callback_ = callback;
+  }
+
+  void unregister_completed_acl_packets_callback() {
+    ASSERT(!acl_credits_callback_.IsEmpty());
+    acl_credits_callback_ = {};
   }
 
   void read_local_name_complete_handler(CommandCompleteView view) {
@@ -190,10 +191,10 @@
     // Query all extended features
     if (page_number < maximum_page_number_) {
       page_number++;
-      hci_->EnqueueCommand(ReadLocalExtendedFeaturesBuilder::Create(page_number),
-                           BindOnce(&Controller::impl::read_local_extended_features_complete_handler,
-                                    common::Unretained(this), std::move(promise)),
-                           module_.GetHandler());
+      hci_->EnqueueCommand(
+          ReadLocalExtendedFeaturesBuilder::Create(page_number),
+          module_.GetHandler()->BindOnceOn(this, &Controller::impl::read_local_extended_features_complete_handler,
+                                           std::move(promise)));
     } else {
       promise.set_value();
     }
@@ -221,11 +222,19 @@
   }
 
   void le_read_buffer_size_handler(CommandCompleteView view) {
-    auto complete_view = LeReadBufferSizeCompleteView::Create(view);
+    auto complete_view = LeReadBufferSizeV1CompleteView::Create(view);
     ASSERT(complete_view.IsValid());
     ErrorCode status = complete_view.GetStatus();
     ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
     le_buffer_size_ = complete_view.GetLeBufferSize();
+
+    // If LE buffer size is zero, then buffers returned by Read_Buffer_Size are shared between BR/EDR and LE.
+    if (le_buffer_size_.total_num_le_packets_ == 0) {
+      ASSERT(acl_buffers_ != 0);
+      le_buffer_size_.total_num_le_packets_ = acl_buffers_ / 2;
+      acl_buffers_ -= le_buffer_size_.total_num_le_packets_;
+      le_buffer_size_.le_data_packet_length_ = acl_buffer_length_;
+    }
   }
 
   void le_read_local_supported_features_handler(CommandCompleteView view) {
@@ -244,6 +253,22 @@
     le_supported_states_ = complete_view.GetLeStates();
   }
 
+  void le_read_connect_list_size_handler(CommandCompleteView view) {
+    auto complete_view = LeReadConnectListSizeCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    le_connect_list_size_ = complete_view.GetConnectListSize();
+  }
+
+  void le_read_resolving_list_size_handler(CommandCompleteView view) {
+    auto complete_view = LeReadResolvingListSizeCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    le_resolving_list_size_ = complete_view.GetResolvingListSize();
+  }
+
   void le_read_maximum_data_length_handler(CommandCompleteView view) {
     auto complete_view = LeReadMaximumDataLengthCompleteView::Create(view);
     ASSERT(complete_view.IsValid());
@@ -344,23 +369,33 @@
 
   void set_event_mask(uint64_t event_mask) {
     std::unique_ptr<SetEventMaskBuilder> packet = SetEventMaskBuilder::Create(event_mask);
-    hci_->EnqueueCommand(std::move(packet),
-                         BindOnce(&Controller::impl::check_status<SetEventMaskCompleteView>, common::Unretained(this)),
-                         module_.GetHandler());
+    hci_->EnqueueCommand(std::move(packet), module_.GetHandler()->BindOnceOn(
+                                                this, &Controller::impl::check_status<SetEventMaskCompleteView>));
+  }
+
+  void write_simple_pairing_mode(Enable enable) {
+    std::unique_ptr<WriteSimplePairingModeBuilder> packet = WriteSimplePairingModeBuilder::Create(enable);
+    hci_->EnqueueCommand(
+        std::move(packet),
+        module_.GetHandler()->BindOnceOn(this, &Controller::impl::check_status<WriteSimplePairingModeCompleteView>));
+  }
+
+  void write_le_host_support(Enable enable, SimultaneousLeHost simultaneous_le_host) {
+    std::unique_ptr<WriteLeHostSupportBuilder> packet = WriteLeHostSupportBuilder::Create(enable, simultaneous_le_host);
+    hci_->EnqueueCommand(
+        std::move(packet),
+        module_.GetHandler()->BindOnceOn(this, &Controller::impl::check_status<WriteLeHostSupportCompleteView>));
   }
 
   void reset() {
     std::unique_ptr<ResetBuilder> packet = ResetBuilder::Create();
     hci_->EnqueueCommand(std::move(packet),
-                         BindOnce(&Controller::impl::check_status<ResetCompleteView>, common::Unretained(this)),
-                         module_.GetHandler());
+                         module_.GetHandler()->BindOnceOn(this, &Controller::impl::check_status<ResetCompleteView>));
   }
 
   void set_event_filter(std::unique_ptr<SetEventFilterBuilder> packet) {
-    hci_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&Controller::impl::check_status<SetEventFilterCompleteView>, common::Unretained(this)),
-        module_.GetHandler());
+    hci_->EnqueueCommand(std::move(packet), module_.GetHandler()->BindOnceOn(
+                                                this, &Controller::impl::check_status<SetEventFilterCompleteView>));
   }
 
   void write_local_name(std::string local_name) {
@@ -371,10 +406,8 @@
     std::copy(std::begin(local_name), std::end(local_name), std::begin(local_name_array));
 
     std::unique_ptr<WriteLocalNameBuilder> packet = WriteLocalNameBuilder::Create(local_name_array);
-    hci_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&Controller::impl::check_status<WriteLocalNameCompleteView>, common::Unretained(this)),
-        module_.GetHandler());
+    hci_->EnqueueCommand(std::move(packet), module_.GetHandler()->BindOnceOn(
+                                                this, &Controller::impl::check_status<WriteLocalNameCompleteView>));
   }
 
   void host_buffer_size(uint16_t host_acl_data_packet_length, uint8_t host_synchronous_data_packet_length,
@@ -382,18 +415,14 @@
     std::unique_ptr<HostBufferSizeBuilder> packet =
         HostBufferSizeBuilder::Create(host_acl_data_packet_length, host_synchronous_data_packet_length,
                                       host_total_num_acl_data_packets, host_total_num_synchronous_data_packets);
-    hci_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&Controller::impl::check_status<HostBufferSizeCompleteView>, common::Unretained(this)),
-        module_.GetHandler());
+    hci_->EnqueueCommand(std::move(packet), module_.GetHandler()->BindOnceOn(
+                                                this, &Controller::impl::check_status<HostBufferSizeCompleteView>));
   }
 
   void le_set_event_mask(uint64_t le_event_mask) {
     std::unique_ptr<LeSetEventMaskBuilder> packet = LeSetEventMaskBuilder::Create(le_event_mask);
-    hci_->EnqueueCommand(
-        std::move(packet),
-        BindOnce(&Controller::impl::check_status<LeSetEventMaskCompleteView>, common::Unretained(this)),
-        module_.GetHandler());
+    hci_->EnqueueCommand(std::move(packet), module_.GetHandler()->BindOnceOn(
+                                                this, &Controller::impl::check_status<LeSetEventMaskCompleteView>));
   }
 
   template <class T>
@@ -548,7 +577,7 @@
       OP_CODE_MAPPING(READ_LE_HOST_SUPPORT)
       OP_CODE_MAPPING(WRITE_LE_HOST_SUPPORT)
       OP_CODE_MAPPING(LE_SET_EVENT_MASK)
-      OP_CODE_MAPPING(LE_READ_BUFFER_SIZE)
+      OP_CODE_MAPPING(LE_READ_BUFFER_SIZE_V1)
       OP_CODE_MAPPING(LE_READ_LOCAL_SUPPORTED_FEATURES)
       OP_CODE_MAPPING(LE_SET_RANDOM_ADDRESS)
       OP_CODE_MAPPING(LE_SET_ADVERTISING_PARAMETERS)
@@ -560,10 +589,10 @@
       OP_CODE_MAPPING(LE_SET_SCAN_ENABLE)
       OP_CODE_MAPPING(LE_CREATE_CONNECTION)
       OP_CODE_MAPPING(LE_CREATE_CONNECTION_CANCEL)
-      OP_CODE_MAPPING(LE_READ_WHITE_LIST_SIZE)
-      OP_CODE_MAPPING(LE_CLEAR_WHITE_LIST)
-      OP_CODE_MAPPING(LE_ADD_DEVICE_TO_WHITE_LIST)
-      OP_CODE_MAPPING(LE_REMOVE_DEVICE_FROM_WHITE_LIST)
+      OP_CODE_MAPPING(LE_READ_CONNECT_LIST_SIZE)
+      OP_CODE_MAPPING(LE_CLEAR_CONNECT_LIST)
+      OP_CODE_MAPPING(LE_ADD_DEVICE_TO_CONNECT_LIST)
+      OP_CODE_MAPPING(LE_REMOVE_DEVICE_FROM_CONNECT_LIST)
       OP_CODE_MAPPING(LE_CONNECTION_UPDATE)
       OP_CODE_MAPPING(LE_SET_HOST_CHANNEL_CLASSIFICATION)
       OP_CODE_MAPPING(LE_READ_CHANNEL_MAP)
@@ -579,7 +608,7 @@
       OP_CODE_MAPPING(LE_TEST_END)
       OP_CODE_MAPPING(ENHANCED_SETUP_SYNCHRONOUS_CONNECTION)
       OP_CODE_MAPPING(ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION)
-      OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CODECS)
+      OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CODECS_V1)
       OP_CODE_MAPPING(READ_SECURE_CONNECTIONS_HOST_SUPPORT)
       OP_CODE_MAPPING(WRITE_SECURE_CONNECTIONS_HOST_SUPPORT)
       OP_CODE_MAPPING(READ_LOCAL_OOB_EXTENDED_DATA)
@@ -631,7 +660,40 @@
       OP_CODE_MAPPING(LE_READ_RF_PATH_COMPENSATION_POWER)
       OP_CODE_MAPPING(LE_WRITE_RF_PATH_COMPENSATION_POWER)
       OP_CODE_MAPPING(LE_SET_PRIVACY_MODE)
+      OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE)
+      OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_SYNC_TRANSFER)
+      OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER)
+      OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS)
+      OP_CODE_MAPPING(LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS)
       OP_CODE_MAPPING(LE_GENERATE_DHKEY_COMMAND)
+      OP_CODE_MAPPING(LE_MODIFY_SLEEP_CLOCK_ACCURACY)
+      OP_CODE_MAPPING(LE_READ_BUFFER_SIZE_V2)
+      OP_CODE_MAPPING(LE_READ_ISO_TX_SYNC)
+      OP_CODE_MAPPING(LE_SET_CIG_PARAMETERS)
+      OP_CODE_MAPPING(LE_CREATE_CIS)
+      OP_CODE_MAPPING(LE_REMOVE_CIG)
+      OP_CODE_MAPPING(LE_ACCEPT_CIS_REQUEST)
+      OP_CODE_MAPPING(LE_REJECT_CIS_REQUEST)
+      OP_CODE_MAPPING(LE_CREATE_BIG)
+      OP_CODE_MAPPING(LE_TERMINATE_BIG)
+      OP_CODE_MAPPING(LE_BIG_CREATE_SYNC)
+      OP_CODE_MAPPING(LE_BIG_TERMINATE_SYNC)
+      OP_CODE_MAPPING(LE_REQUEST_PEER_SCA)
+      OP_CODE_MAPPING(LE_SETUP_ISO_DATA_PATH)
+      OP_CODE_MAPPING(LE_REMOVE_ISO_DATA_PATH)
+      OP_CODE_MAPPING(LE_SET_HOST_FEATURE)
+      OP_CODE_MAPPING(LE_READ_ISO_LINK_QUALITY)
+      OP_CODE_MAPPING(LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL)
+      OP_CODE_MAPPING(LE_READ_REMOTE_TRANSMIT_POWER_LEVEL)
+      OP_CODE_MAPPING(LE_SET_PATH_LOSS_REPORTING_PARAMETERS)
+      OP_CODE_MAPPING(LE_SET_PATH_LOSS_REPORTING_ENABLE)
+      OP_CODE_MAPPING(LE_SET_TRANSMIT_POWER_REPORTING_ENABLE)
+      OP_CODE_MAPPING(SET_ECOSYSTEM_BASE_INTERVAL)
+      OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CODECS_V2)
+      OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES)
+      OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CONTROLLER_DELAY)
+      OP_CODE_MAPPING(CONFIGURE_DATA_PATH)
+
       // vendor specific
       case OpCode::LE_GET_VENDOR_CAPABILITIES:
         return vendor_capabilities_.is_supported_ == 0x01;
@@ -668,8 +730,7 @@
 
   HciLayer* hci_;
 
-  Callback<void(uint16_t, uint16_t)> acl_credits_callback_;
-  Handler* acl_credits_handler_ = nullptr;
+  CompletedAclPacketsCallback acl_credits_callback_{};
   LocalVersionInformation local_version_information_;
   std::array<uint8_t, 64> local_supported_commands_;
   uint64_t local_supported_features_;
@@ -684,6 +745,8 @@
   LeBufferSize le_buffer_size_;
   uint64_t le_local_supported_features_;
   uint64_t le_supported_states_;
+  uint8_t le_connect_list_size_;
+  uint8_t le_resolving_list_size_;
   LeMaximumDataLength le_maximum_data_length_;
   uint16_t le_maximum_advertising_data_length_;
   uint8_t le_number_supported_advertising_sets_;
@@ -694,9 +757,12 @@
 
 Controller::~Controller() = default;
 
-void Controller::RegisterCompletedAclPacketsCallback(Callback<void(uint16_t /* handle */, uint16_t /* packets */)> cb,
-                                                     Handler* handler) {
-  impl_->RegisterCompletedAclPacketsCallback(cb, handler);  // TODO hsz: why here?
+void Controller::RegisterCompletedAclPacketsCallback(CompletedAclPacketsCallback cb) {
+  CallOn(impl_.get(), &impl::register_completed_acl_packets_callback, cb);
+}
+
+void Controller::UnregisterCompletedAclPacketsCallback() {
+  CallOn(impl_.get(), &impl::unregister_completed_acl_packets_callback);
 }
 
 std::string Controller::GetControllerLocalName() const {
@@ -747,41 +813,41 @@
 }
 
 void Controller::SetEventMask(uint64_t event_mask) {
-  GetHandler()->Post(common::BindOnce(&impl::set_event_mask, common::Unretained(impl_.get()), event_mask));
+  CallOn(impl_.get(), &impl::set_event_mask, event_mask);
 }
 
 void Controller::Reset() {
-  GetHandler()->Post(common::BindOnce(&impl::reset, common::Unretained(impl_.get())));
+  CallOn(impl_.get(), &impl::reset);
 }
 
 void Controller::SetEventFilterClearAll() {
   std::unique_ptr<SetEventFilterClearAllBuilder> packet = SetEventFilterClearAllBuilder::Create();
-  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+  CallOn(impl_.get(), &impl::set_event_filter, std::move(packet));
 }
 
 void Controller::SetEventFilterInquiryResultAllDevices() {
   std::unique_ptr<SetEventFilterInquiryResultAllDevicesBuilder> packet =
       SetEventFilterInquiryResultAllDevicesBuilder::Create();
-  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+  CallOn(impl_.get(), &impl::set_event_filter, std::move(packet));
 }
 
 void Controller::SetEventFilterInquiryResultClassOfDevice(ClassOfDevice class_of_device,
                                                           ClassOfDevice class_of_device_mask) {
   std::unique_ptr<SetEventFilterInquiryResultClassOfDeviceBuilder> packet =
       SetEventFilterInquiryResultClassOfDeviceBuilder::Create(class_of_device, class_of_device_mask);
-  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+  CallOn(impl_.get(), &impl::set_event_filter, std::move(packet));
 }
 
 void Controller::SetEventFilterInquiryResultAddress(Address address) {
   std::unique_ptr<SetEventFilterInquiryResultAddressBuilder> packet =
       SetEventFilterInquiryResultAddressBuilder::Create(address);
-  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+  CallOn(impl_.get(), &impl::set_event_filter, std::move(packet));
 }
 
 void Controller::SetEventFilterConnectionSetupAllDevices(AutoAcceptFlag auto_accept_flag) {
   std::unique_ptr<SetEventFilterConnectionSetupAllDevicesBuilder> packet =
       SetEventFilterConnectionSetupAllDevicesBuilder::Create(auto_accept_flag);
-  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+  CallOn(impl_.get(), &impl::set_event_filter, std::move(packet));
 }
 
 void Controller::SetEventFilterConnectionSetupClassOfDevice(ClassOfDevice class_of_device,
@@ -790,30 +856,34 @@
   std::unique_ptr<SetEventFilterConnectionSetupClassOfDeviceBuilder> packet =
       SetEventFilterConnectionSetupClassOfDeviceBuilder::Create(class_of_device, class_of_device_mask,
                                                                 auto_accept_flag);
-  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+  CallOn(impl_.get(), &impl::set_event_filter, std::move(packet));
 }
 
 void Controller::SetEventFilterConnectionSetupAddress(Address address, AutoAcceptFlag auto_accept_flag) {
   std::unique_ptr<SetEventFilterConnectionSetupAddressBuilder> packet =
       SetEventFilterConnectionSetupAddressBuilder::Create(address, auto_accept_flag);
-  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+  CallOn(impl_.get(), &impl::set_event_filter, std::move(packet));
 }
 
 void Controller::WriteLocalName(std::string local_name) {
   impl_->local_name_ = local_name;
-  GetHandler()->Post(common::BindOnce(&impl::write_local_name, common::Unretained(impl_.get()), local_name));
+  CallOn(impl_.get(), &impl::write_local_name, local_name);
 }
 
 void Controller::HostBufferSize(uint16_t host_acl_data_packet_length, uint8_t host_synchronous_data_packet_length,
                                 uint16_t host_total_num_acl_data_packets,
                                 uint16_t host_total_num_synchronous_data_packets) {
-  GetHandler()->Post(common::BindOnce(&impl::host_buffer_size, common::Unretained(impl_.get()),
-                                      host_acl_data_packet_length, host_synchronous_data_packet_length,
-                                      host_total_num_acl_data_packets, host_total_num_synchronous_data_packets));
+  CallOn(
+      impl_.get(),
+      &impl::host_buffer_size,
+      host_acl_data_packet_length,
+      host_synchronous_data_packet_length,
+      host_total_num_acl_data_packets,
+      host_total_num_synchronous_data_packets);
 }
 
 void Controller::LeSetEventMask(uint64_t le_event_mask) {
-  GetHandler()->Post(common::BindOnce(&impl::le_set_event_mask, common::Unretained(impl_.get()), le_event_mask));
+  CallOn(impl_.get(), &impl::le_set_event_mask, le_event_mask);
 }
 
 LeBufferSize Controller::GetControllerLeBufferSize() const {
@@ -828,6 +898,14 @@
   return impl_->le_supported_states_;
 }
 
+uint8_t Controller::GetControllerLeConnectListSize() const {
+  return impl_->le_connect_list_size_;
+}
+
+uint8_t Controller::GetControllerLeResolvingListSize() const {
+  return impl_->le_resolving_list_size_;
+}
+
 LeMaximumDataLength Controller::GetControllerLeMaximumDataLength() const {
   return impl_->le_maximum_data_length_;
 }
diff --git a/gd/hci/controller.h b/gd/hci/controller.h
index d8d39ca..33193fd 100644
--- a/gd/hci/controller.h
+++ b/gd/hci/controller.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include "common/callback.h"
+#include "common/contextual_callback.h"
 #include "hci/address.h"
 #include "hci/hci_packets.h"
 #include "module.h"
@@ -31,8 +31,11 @@
   virtual ~Controller();
   DISALLOW_COPY_AND_ASSIGN(Controller);
 
-  virtual void RegisterCompletedAclPacketsCallback(
-      common::Callback<void(uint16_t /* handle */, uint16_t /* num_packets */)> cb, os::Handler* handler);
+  using CompletedAclPacketsCallback =
+      common::ContextualCallback<void(uint16_t /* handle */, uint16_t /* num_packets */)>;
+  virtual void RegisterCompletedAclPacketsCallback(CompletedAclPacketsCallback cb);
+
+  virtual void UnregisterCompletedAclPacketsCallback();
 
   virtual std::string GetControllerLocalName() const;
 
@@ -92,6 +95,10 @@
 
   virtual uint64_t GetControllerLeSupportedStates() const;
 
+  virtual uint8_t GetControllerLeConnectListSize() const;
+
+  virtual uint8_t GetControllerLeResolvingListSize() const;
+
   virtual LeMaximumDataLength GetControllerLeMaximumDataLength() const;
 
   virtual uint16_t GetControllerLeMaximumAdvertisingDataLength() const;
@@ -104,6 +111,8 @@
 
   static const ModuleFactory Factory;
 
+  static constexpr uint64_t kDefaultEventMask = 0x3dbfffffffffffff;
+
  protected:
   void ListDependencies(ModuleList* list) override;
 
diff --git a/gd/hci/controller_test.cc b/gd/hci/controller_test.cc
index 9d109f8..b09a7b7 100644
--- a/gd/hci/controller_test.cc
+++ b/gd/hci/controller_test.cc
@@ -57,21 +57,21 @@
 class TestHciLayer : public HciLayer {
  public:
   void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+                      common::ContextualOnceCallback<void(CommandCompleteView)> on_complete) override {
     GetHandler()->Post(common::BindOnce(&TestHciLayer::HandleCommand, common::Unretained(this), std::move(command),
-                                        std::move(on_complete), common::Unretained(handler)));
+                                        std::move(on_complete)));
   }
 
   void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+                      common::ContextualOnceCallback<void(CommandStatusView)> on_status) override {
     EXPECT_TRUE(false) << "Controller properties should not generate Command Status";
   }
 
   void HandleCommand(std::unique_ptr<CommandPacketBuilder> command_builder,
-                     common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) {
+                     common::ContextualOnceCallback<void(CommandCompleteView)> on_complete) {
     auto packet_view = GetPacketView(std::move(command_builder));
     CommandPacketView command = CommandPacketView::Create(packet_view);
-    ASSERT(command.IsValid());
+    ASSERT_TRUE(command.IsValid());
 
     uint8_t num_packets = 1;
     std::unique_ptr<packet::BasePacketBuilder> event_builder;
@@ -108,7 +108,7 @@
       } break;
       case (OpCode::READ_LOCAL_EXTENDED_FEATURES): {
         ReadLocalExtendedFeaturesView read_command = ReadLocalExtendedFeaturesView::Create(command);
-        ASSERT(read_command.IsValid());
+        ASSERT_TRUE(read_command.IsValid());
         uint8_t page_bumber = read_command.GetPageNumber();
         uint64_t lmp_features = 0x012345678abcdef;
         lmp_features += page_bumber;
@@ -123,11 +123,11 @@
       case (OpCode::READ_BD_ADDR): {
         event_builder = ReadBdAddrCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, Address::kAny);
       } break;
-      case (OpCode::LE_READ_BUFFER_SIZE): {
+      case (OpCode::LE_READ_BUFFER_SIZE_V1): {
         LeBufferSize le_buffer_size;
         le_buffer_size.le_data_packet_length_ = 0x16;
         le_buffer_size.total_num_le_packets_ = 0x08;
-        event_builder = LeReadBufferSizeCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, le_buffer_size);
+        event_builder = LeReadBufferSizeV1CompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, le_buffer_size);
       } break;
       case (OpCode::LE_READ_LOCAL_SUPPORTED_FEATURES): {
         event_builder =
@@ -173,7 +173,12 @@
         event_builder = LeGetVendorCapabilitiesCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS,
                                                                        base_vendor_capabilities, std::move(payload));
       } break;
-      case (OpCode::SET_EVENT_MASK):
+      case (OpCode::SET_EVENT_MASK): {
+        auto view = SetEventMaskView::Create(command);
+        ASSERT_TRUE(view.IsValid());
+        event_mask = view.GetEventMask();
+        event_builder = SetEventMaskCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS);
+      } break;
       case (OpCode::RESET):
       case (OpCode::SET_EVENT_FILTER):
       case (OpCode::HOST_BUFFER_SIZE):
@@ -187,23 +192,21 @@
     }
     auto packet = GetPacketView(std::move(event_builder));
     EventPacketView event = EventPacketView::Create(packet);
-    ASSERT(event.IsValid());
+    ASSERT_TRUE(event.IsValid());
     CommandCompleteView command_complete = CommandCompleteView::Create(event);
-    ASSERT(command_complete.IsValid());
-    handler->Post(common::BindOnce(std::move(on_complete), std::move(command_complete)));
+    ASSERT_TRUE(command_complete.IsValid());
+    on_complete.Invoke(std::move(command_complete));
   }
 
-  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
-                            os::Handler* handler) override {
+  void RegisterEventHandler(EventCode event_code,
+                            common::ContextualCallback<void(EventPacketView)> event_handler) override {
     EXPECT_EQ(event_code, EventCode::NUMBER_OF_COMPLETED_PACKETS) << "Only NUMBER_OF_COMPLETED_PACKETS is needed";
     number_of_completed_packets_callback_ = event_handler;
-    client_handler_ = handler;
   }
 
   void UnregisterEventHandler(EventCode event_code) override {
     EXPECT_EQ(event_code, EventCode::NUMBER_OF_COMPLETED_PACKETS) << "Only NUMBER_OF_COMPLETED_PACKETS is needed";
     number_of_completed_packets_callback_ = {};
-    client_handler_ = nullptr;
   }
 
   void IncomingCredit() {
@@ -218,8 +221,8 @@
     auto event_builder = NumberOfCompletedPacketsBuilder::Create(completed_packets);
     auto packet = GetPacketView(std::move(event_builder));
     EventPacketView event = EventPacketView::Create(packet);
-    ASSERT(event.IsValid());
-    client_handler_->Post(common::BindOnce(number_of_completed_packets_callback_, event));
+    ASSERT_TRUE(event.IsValid());
+    number_of_completed_packets_callback_.Invoke(event);
   }
 
   CommandPacketView GetCommand(OpCode op_code) {
@@ -232,7 +235,10 @@
         break;
       }
     }
-    ASSERT(command_queue_.size() > 0);
+    EXPECT_TRUE(command_queue_.size() > 0);
+    if (command_queue_.empty()) {
+      return CommandPacketView::Create(std::make_shared<std::vector<uint8_t>>());
+    }
     CommandPacketView command = command_queue_.front();
     EXPECT_EQ(command.GetOpCode(), op_code);
     command_queue_.pop();
@@ -247,10 +253,10 @@
   constexpr static uint8_t synchronous_data_packet_length = 60;
   constexpr static uint16_t total_num_acl_data_packets = 10;
   constexpr static uint16_t total_num_synchronous_data_packets = 12;
+  uint64_t event_mask = 0;
 
  private:
-  common::Callback<void(EventPacketView)> number_of_completed_packets_callback_;
-  os::Handler* client_handler_;
+  common::ContextualCallback<void(EventPacketView)> number_of_completed_packets_callback_;
   std::queue<CommandPacketView> command_queue_;
   mutable std::mutex mutex_;
   std::condition_variable not_empty_;
@@ -280,7 +286,6 @@
 TEST_F(ControllerTest, startup_teardown) {}
 
 TEST_F(ControllerTest, read_controller_info) {
-  std::promise<void> callback_completed;
   ASSERT_EQ(controller_->GetControllerAclPacketLength(), test_hci_layer_->acl_data_packet_length);
   ASSERT_EQ(controller_->GetControllerNumAclPacketBuffers(), test_hci_layer_->total_num_acl_data_packets);
   ASSERT_EQ(controller_->GetControllerScoPacketLength(), test_hci_layer_->synchronous_data_packet_length);
@@ -323,18 +328,19 @@
 }
 
 TEST_F(ControllerTest, send_set_event_mask_command) {
-  controller_->SetEventMask(0x00001FFFFFFFFFFF);
-  auto packet = test_hci_layer_->GetCommand(OpCode::SET_EVENT_MASK);
-  auto command = SetEventMaskView::Create(packet);
-  ASSERT(command.IsValid());
-  ASSERT_EQ(command.GetEventMask(), 0x00001FFFFFFFFFFF);
+  uint64_t new_event_mask = test_hci_layer_->event_mask - 1;
+  controller_->SetEventMask(new_event_mask);
+  // Send another command to make sure it was applied
+  controller_->Reset();
+  auto packet = test_hci_layer_->GetCommand(OpCode::RESET);
+  ASSERT_EQ(new_event_mask, test_hci_layer_->event_mask);
 }
 
 TEST_F(ControllerTest, send_reset_command) {
   controller_->Reset();
   auto packet = test_hci_layer_->GetCommand(OpCode::RESET);
   auto command = ResetView::Create(packet);
-  ASSERT(command.IsValid());
+  ASSERT_TRUE(command.IsValid());
 }
 
 TEST_F(ControllerTest, send_set_event_filter_command) {
@@ -343,7 +349,7 @@
   auto set_event_filter_view1 = SetEventFilterView::Create(packet);
   auto set_event_filter_inquiry_result_view1 = SetEventFilterInquiryResultView::Create(set_event_filter_view1);
   auto command1 = SetEventFilterInquiryResultAllDevicesView::Create(set_event_filter_inquiry_result_view1);
-  ASSERT(command1.IsValid());
+  ASSERT_TRUE(command1.IsValid());
 
   ClassOfDevice class_of_device({0xab, 0xcd, 0xef});
   ClassOfDevice class_of_device_mask({0x12, 0x34, 0x56});
@@ -352,7 +358,7 @@
   auto set_event_filter_view2 = SetEventFilterView::Create(packet);
   auto set_event_filter_inquiry_result_view2 = SetEventFilterInquiryResultView::Create(set_event_filter_view2);
   auto command2 = SetEventFilterInquiryResultClassOfDeviceView::Create(set_event_filter_inquiry_result_view2);
-  ASSERT(command2.IsValid());
+  ASSERT_TRUE(command2.IsValid());
   ASSERT_EQ(command2.GetClassOfDevice(), class_of_device);
 
   Address bdaddr({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});
@@ -361,7 +367,7 @@
   auto set_event_filter_view3 = SetEventFilterView::Create(packet);
   auto set_event_filter_connection_setup_view = SetEventFilterConnectionSetupView::Create(set_event_filter_view3);
   auto command3 = SetEventFilterConnectionSetupAddressView::Create(set_event_filter_connection_setup_view);
-  ASSERT(command3.IsValid());
+  ASSERT_TRUE(command3.IsValid());
   ASSERT_EQ(command3.GetAddress(), bdaddr);
 }
 
@@ -369,7 +375,7 @@
   controller_->HostBufferSize(0xFF00, 0xF1, 0xFF02, 0xFF03);
   auto packet = test_hci_layer_->GetCommand(OpCode::HOST_BUFFER_SIZE);
   auto command = HostBufferSizeView::Create(packet);
-  ASSERT(command.IsValid());
+  ASSERT_TRUE(command.IsValid());
   ASSERT_EQ(command.GetHostAclDataPacketLength(), 0xFF00);
   ASSERT_EQ(command.GetHostSynchronousDataPacketLength(), 0xF1);
   ASSERT_EQ(command.GetHostTotalNumAclDataPackets(), 0xFF02);
@@ -380,7 +386,7 @@
   controller_->LeSetEventMask(0x000000000000001F);
   auto packet = test_hci_layer_->GetCommand(OpCode::LE_SET_EVENT_MASK);
   auto command = LeSetEventMaskView::Create(packet);
-  ASSERT(command.IsValid());
+  ASSERT_TRUE(command.IsValid());
   ASSERT_EQ(command.GetLeEventMask(), 0x000000000000001F);
 }
 
@@ -447,13 +453,25 @@
 }
 
 TEST_F(ControllerTest, aclCreditCallbacksTest) {
-  controller_->RegisterCompletedAclPacketsCallback(common::Bind(&CheckReceivedCredits), client_handler_);
+  controller_->RegisterCompletedAclPacketsCallback(client_handler_->Bind(&CheckReceivedCredits));
 
   test_hci_layer_->IncomingCredit();
 
   credits1_set.get_future().wait();
   credits2_set.get_future().wait();
 }
+
+TEST_F(ControllerTest, aclCreditCallbackListenerUnregistered) {
+  os::Thread thread("test_thread", os::Thread::Priority::NORMAL);
+  os::Handler handler(&thread);
+  controller_->RegisterCompletedAclPacketsCallback(handler.Bind(&CheckReceivedCredits));
+
+  handler.Clear();
+  handler.WaitUntilStopped(std::chrono::milliseconds(100));
+  controller_->UnregisterCompletedAclPacketsCallback();
+
+  test_hci_layer_->IncomingCredit();
+}
 }  // namespace
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/facade.cc b/gd/hci/facade.cc
deleted file mode 100644
index 3ed56d0..0000000
--- a/gd/hci/facade.cc
+++ /dev/null
@@ -1,752 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "hci/facade.h"
-
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-
-#include "common/bind.h"
-#include "common/blocking_queue.h"
-#include "grpc/grpc_event_queue.h"
-#include "hci/acl_manager.h"
-#include "hci/classic_security_manager.h"
-#include "hci/controller.h"
-#include "hci/facade.grpc.pb.h"
-#include "hci/hci_layer.h"
-#include "hci/hci_packets.h"
-#include "packet/raw_builder.h"
-
-using ::grpc::ServerAsyncResponseWriter;
-using ::grpc::ServerAsyncWriter;
-using ::grpc::ServerContext;
-
-using ::bluetooth::packet::RawBuilder;
-
-namespace bluetooth {
-namespace hci {
-
-class AclManagerFacadeService : public AclManagerFacade::Service,
-                                public ::bluetooth::hci::ConnectionCallbacks,
-                                public ::bluetooth::hci::ConnectionManagementCallbacks,
-                                public ::bluetooth::hci::AclManagerCallbacks {
- public:
-  AclManagerFacadeService(AclManager* acl_manager, Controller* controller, HciLayer* hci_layer,
-                          ::bluetooth::os::Handler* facade_handler)
-      : acl_manager_(acl_manager), controller_(controller), hci_layer_(hci_layer), facade_handler_(facade_handler) {
-    acl_manager_->RegisterCallbacks(this, facade_handler_);
-    acl_manager_->RegisterAclManagerCallbacks(this, facade_handler_);
-  }
-
-  ::grpc::Status SetPageScanMode(::grpc::ServerContext* context, const ::bluetooth::hci::PageScanMode* request,
-                                 ::google::protobuf::Empty* response) override {
-    ScanEnable scan_enable = request->enabled() ? ScanEnable::PAGE_SCAN_ONLY : ScanEnable::NO_SCANS;
-    std::promise<void> promise;
-    auto future = promise.get_future();
-    hci_layer_->EnqueueCommand(
-        WriteScanEnableBuilder::Create(scan_enable),
-        common::BindOnce([](std::promise<void> promise, CommandCompleteView) { promise.set_value(); },
-                         std::move(promise)),
-        facade_handler_);
-    future.wait();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status Connect(::grpc::ServerContext* context, const facade::BluetoothAddress* request,
-                         ::google::protobuf::Empty* response) override {
-    Address peer;
-    ASSERT(Address::FromString(request->address(), peer));
-    acl_manager_->CreateConnection(peer);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status Disconnect(::grpc::ServerContext* context, const facade::BluetoothAddress* request,
-                            ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-    Address peer;
-    Address::FromString(request->address(), peer);
-    auto connection = acl_connections_.find(request->address());
-    if (connection == acl_connections_.end()) {
-      LOG_ERROR("Invalid address");
-      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid address");
-    } else {
-      connection->second->Disconnect(DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
-      return ::grpc::Status::OK;
-    }
-  }
-
-  ::grpc::Status AuthenticationRequested(::grpc::ServerContext* context, const facade::BluetoothAddress* request,
-                                         ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-    Address peer;
-    Address::FromString(request->address(), peer);
-    auto connection = acl_connections_.find(request->address());
-    if (connection == acl_connections_.end()) {
-      LOG_ERROR("Invalid address");
-      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid address");
-    } else {
-      connection->second->AuthenticationRequested();
-      return ::grpc::Status::OK;
-    }
-  };
-
-  ::grpc::Status SendAclData(::grpc::ServerContext* context, const AclData* request,
-                             ::google::protobuf::Empty* response) override {
-    std::promise<void> promise;
-    auto future = promise.get_future();
-    {
-      std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-      acl_connections_[request->remote().address()]->GetAclQueueEnd()->RegisterEnqueue(
-          facade_handler_, common::Bind(&AclManagerFacadeService::enqueue_packet, common::Unretained(this),
-                                        common::Unretained(request), common::Passed(std::move(promise))));
-    }
-    future.wait();
-    return ::grpc::Status::OK;
-  }
-
-  std::unique_ptr<BasePacketBuilder> enqueue_packet(const AclData* request, std::promise<void> promise) {
-    {
-      std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-      acl_connections_[request->remote().address()]->GetAclQueueEnd()->UnregisterEnqueue();
-    }
-    std::string req_string = request->payload();
-    std::unique_ptr<RawBuilder> packet = std::make_unique<RawBuilder>();
-    packet->AddOctets(std::vector<uint8_t>(req_string.begin(), req_string.end()));
-    promise.set_value();
-    return packet;
-  }
-
-  ::grpc::Status FetchAclData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                              ::grpc::ServerWriter<AclData>* writer) override {
-    {
-      std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-      for (const auto& connection : acl_connections_) {
-        auto remote_address = connection.second->GetAddress().ToString();
-        connection.second->GetAclQueueEnd()->RegisterDequeue(
-            facade_handler_,
-            common::Bind(&AclManagerFacadeService::on_incoming_acl, common::Unretained(this), remote_address));
-      }
-      fetching_acl_data_ = true;
-    }
-    auto status = pending_acl_data_.RunLoop(context, writer);
-    {
-      std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-      fetching_acl_data_ = false;
-      for (const auto& connection : acl_connections_) {
-        connection.second->GetAclQueueEnd()->UnregisterDequeue();
-      }
-    }
-
-    return status;
-  }
-
-  ::grpc::Status TestInternalHciCommands(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                         ::google::protobuf::Empty* response) {
-    LocalVersionInformation local_version_information = controller_->GetControllerLocalVersionInformation();
-    LOG_DEBUG("local name : %s", controller_->GetControllerLocalName().c_str());
-    controller_->WriteLocalName("Device Under Test");
-    LOG_DEBUG("new local name : %s", controller_->GetControllerLocalName().c_str());
-    LOG_DEBUG("manufacturer name : %d", local_version_information.manufacturer_name_);
-    LOG_DEBUG("hci version : %x", (uint16_t)local_version_information.hci_version_);
-    LOG_DEBUG("lmp version : %x", (uint16_t)local_version_information.lmp_version_);
-    LOG_DEBUG("supported commands : %x", controller_->GetControllerLocalSupportedCommands()[0]);
-    LOG_DEBUG("local extended features :");
-
-    controller_->SetEventMask(0x00001FFFFFFFFFFF);
-    controller_->SetEventFilterInquiryResultAllDevices();
-    ClassOfDevice class_of_device({0xab, 0xcd, 0xef});
-    ClassOfDevice class_of_device_mask({0x12, 0x34, 0x56});
-    controller_->SetEventFilterInquiryResultClassOfDevice(class_of_device, class_of_device_mask);
-    Address bdaddr({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});
-    controller_->SetEventFilterInquiryResultAddress(bdaddr);
-    controller_->SetEventFilterConnectionSetupAllDevices(AutoAcceptFlag::AUTO_ACCEPT_OFF);
-    controller_->SetEventFilterConnectionSetupClassOfDevice(class_of_device, class_of_device_mask,
-                                                            AutoAcceptFlag::AUTO_ACCEPT_ON_ROLE_SWITCH_DISABLED);
-    controller_->SetEventFilterConnectionSetupAddress(bdaddr, AutoAcceptFlag::AUTO_ACCEPT_ON_ROLE_SWITCH_ENABLED);
-    controller_->SetEventFilterClearAll();
-    controller_->HostBufferSize(0xFF00, 0xF1, 0xFF02, 0xFF03);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status TestInternalHciLeCommands(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                           ::google::protobuf::Empty* response) {
-    LOG_DEBUG("le data packet length : %d", controller_->GetControllerLeBufferSize().le_data_packet_length_);
-    LOG_DEBUG("total num le packets : %d", controller_->GetControllerLeBufferSize().total_num_le_packets_);
-    LOG_DEBUG("le supported max tx octets : %d",
-              controller_->GetControllerLeMaximumDataLength().supported_max_tx_octets_);
-    LOG_DEBUG("le supported max tx times : %d", controller_->GetControllerLeMaximumDataLength().supported_max_tx_time_);
-    LOG_DEBUG("le supported max rx octets : %d",
-              controller_->GetControllerLeMaximumDataLength().supported_max_rx_octets_);
-    LOG_DEBUG("le supported max rx times : %d", controller_->GetControllerLeMaximumDataLength().supported_max_rx_time_);
-    LOG_DEBUG("le maximum advertising data length %d", controller_->GetControllerLeMaximumAdvertisingDataLength());
-    LOG_DEBUG("le number of supported advertising sets %d",
-              controller_->GetControllerLeNumberOfSupportedAdverisingSets());
-
-    controller_->LeSetEventMask(0x000000000000001F);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status TestClassicConnectionManagementCommands(::grpc::ServerContext* context,
-                                                         const facade::BluetoothAddress* request,
-                                                         ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-    Address peer;
-    Address::FromString(request->address(), peer);
-    auto connection = acl_connections_.find(request->address());
-    if (connection == acl_connections_.end()) {
-      LOG_ERROR("Invalid address");
-      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid address");
-    } else {
-      // TODO add individual grpc command if necessary
-      connection->second->RoleDiscovery();
-      connection->second->WriteLinkPolicySettings(0x07);
-      connection->second->ReadLinkPolicySettings();
-      connection->second->SniffSubrating(0x1234, 0x1234, 0x1234);
-      connection->second->WriteAutomaticFlushTimeout(0x07FF);
-      connection->second->ReadAutomaticFlushTimeout();
-      connection->second->ReadTransmitPowerLevel(TransmitPowerLevelType::CURRENT);
-      connection->second->ReadTransmitPowerLevel(TransmitPowerLevelType::MAXIMUM);
-      connection->second->WriteLinkSupervisionTimeout(0x5678);
-      connection->second->ReadLinkSupervisionTimeout();
-      connection->second->ReadFailedContactCounter();
-      connection->second->ResetFailedContactCounter();
-      connection->second->ReadLinkQuality();
-      connection->second->ReadAfhChannelMap();
-      connection->second->ReadRssi();
-      connection->second->ReadClock(WhichClock::LOCAL);
-      connection->second->ReadClock(WhichClock::PICONET);
-
-      connection->second->ChangeConnectionPacketType(0xEE1C);
-      connection->second->SetConnectionEncryption(Enable::ENABLED);
-      connection->second->ChangeConnectionLinkKey();
-      connection->second->ReadClockOffset();
-      connection->second->HoldMode(0x0500, 0x0020);
-      connection->second->SniffMode(0x0500, 0x0020, 0x0040, 0x0014);
-      connection->second->ExitSniffMode();
-      connection->second->QosSetup(ServiceType::BEST_EFFORT, 0x1234, 0x1233, 0x1232, 0x1231);
-      connection->second->FlowSpecification(FlowDirection::OUTGOING_FLOW, ServiceType::BEST_EFFORT, 0x1234, 0x1233,
-                                            0x1232, 0x1231);
-      connection->second->Flush();
-
-      acl_manager_->MasterLinkKey(KeyFlag::TEMPORARY);
-      acl_manager_->SwitchRole(peer, Role::MASTER);
-      acl_manager_->WriteDefaultLinkPolicySettings(0x07);
-      acl_manager_->ReadDefaultLinkPolicySettings();
-      return ::grpc::Status::OK;
-    }
-  }
-
-  void on_incoming_acl(std::string address) {
-    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-    auto connection = acl_connections_.find(address);
-    if (connection == acl_connections_.end()) {
-      LOG_ERROR("Invalid address");
-      return;
-    }
-
-    auto packet = connection->second->GetAclQueueEnd()->TryDequeue();
-    auto acl_packet = AclPacketView::Create(*packet);
-    AclData acl_data;
-    acl_data.mutable_remote()->set_address(address);
-    std::string data(acl_packet.begin(), acl_packet.end());
-    acl_data.set_payload(data);
-    pending_acl_data_.OnIncomingEvent(acl_data);
-  }
-
-  void OnConnectSuccess(std::unique_ptr<::bluetooth::hci::AclConnection> connection) override {
-    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-    auto addr = connection->GetAddress();
-    std::shared_ptr<::bluetooth::hci::AclConnection> shared_connection = std::move(connection);
-    acl_connections_.emplace(addr.ToString(), shared_connection);
-    if (fetching_acl_data_) {
-      auto remote_address = shared_connection->GetAddress().ToString();
-      shared_connection->GetAclQueueEnd()->RegisterDequeue(
-          facade_handler_,
-          common::Bind(&AclManagerFacadeService::on_incoming_acl, common::Unretained(this), remote_address));
-    }
-    shared_connection->RegisterDisconnectCallback(
-        common::BindOnce(&AclManagerFacadeService::on_disconnect, common::Unretained(this), addr.ToString()),
-        facade_handler_);
-    shared_connection->RegisterCallbacks(this, facade_handler_);
-    {
-      ConnectionEvent response;
-      response.mutable_remote()->set_address(shared_connection->GetAddress().ToString());
-      response.set_connection_handle(shared_connection->GetHandle());
-      pending_connection_complete_.OnIncomingEvent(response);
-    }
-  }
-
-  void OnMasterLinkKeyComplete(uint16_t connection_handle, KeyFlag key_flag) override {
-    LOG_DEBUG("OnMasterLinkKeyComplete connection_handle:%d", connection_handle);
-  }
-
-  void OnRoleChange(Address bd_addr, Role new_role) override {
-    LOG_DEBUG("OnRoleChange bd_addr:%s, new_role:%d", bd_addr.ToString().c_str(), (uint8_t)new_role);
-  }
-
-  void OnReadDefaultLinkPolicySettingsComplete(uint16_t default_link_policy_settings) override {
-    LOG_DEBUG("OnReadDefaultLinkPolicySettingsComplete default_link_policy_settings:%d", default_link_policy_settings);
-  }
-
-  void on_disconnect(const std::string& address, ErrorCode code) {
-    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-    auto connection = acl_connections_.find(address);
-    if (connection != acl_connections_.end()) {
-      if (fetching_acl_data_) {
-        connection->second->GetAclQueueEnd()->UnregisterDequeue();
-      }
-      connection->second->Finish();
-    }
-    acl_connections_.erase(address);
-    DisconnectionEvent event;
-    event.mutable_remote()->set_address(address);
-    event.set_reason(static_cast<uint32_t>(code));
-    pending_disconnection_.OnIncomingEvent(event);
-  }
-
-  ::grpc::Status FetchConnectionComplete(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                         ::grpc::ServerWriter<ConnectionEvent>* writer) override {
-    return pending_connection_complete_.RunLoop(context, writer);
-  };
-
-  void OnConnectFail(Address address, ::bluetooth::hci::ErrorCode reason) override {
-    ConnectionFailedEvent event;
-    event.mutable_remote()->set_address(address.ToString());
-    event.set_reason(static_cast<uint32_t>(reason));
-    pending_connection_failed_.OnIncomingEvent(event);
-  }
-
-  void OnConnectionPacketTypeChanged(uint16_t packet_type) override {
-    LOG_DEBUG("OnConnectionPacketTypeChanged packet_type:%d", packet_type);
-  }
-
-  void OnAuthenticationComplete() override {
-    LOG_DEBUG("OnAuthenticationComplete");
-  }
-
-  void OnEncryptionChange(EncryptionEnabled enabled) override {
-    LOG_DEBUG("OnConnectionPacketTypeChanged enabled:%d", (uint8_t)enabled);
-  }
-
-  void OnChangeConnectionLinkKeyComplete() override {
-    LOG_DEBUG("OnChangeConnectionLinkKeyComplete");
-  };
-
-  void OnReadClockOffsetComplete(uint16_t clock_offset) override {
-    LOG_DEBUG("OnReadClockOffsetComplete clock_offset:%d", clock_offset);
-  };
-
-  void OnModeChange(Mode current_mode, uint16_t interval) override {
-    LOG_DEBUG("OnModeChange Mode:%d, interval:%d", (uint8_t)current_mode, interval);
-  };
-
-  void OnQosSetupComplete(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
-                          uint32_t delay_variation) override {
-    LOG_DEBUG("OnQosSetupComplete service_type:%d, token_rate:%d, peak_bandwidth:%d, latency:%d, delay_variation:%d",
-              (uint8_t)service_type, token_rate, peak_bandwidth, latency, delay_variation);
-  }
-
-  void OnFlowSpecificationComplete(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
-                                   uint32_t token_bucket_size, uint32_t peak_bandwidth,
-                                   uint32_t access_latency) override {
-    LOG_DEBUG(
-        "OnFlowSpecificationComplete flow_direction:%d. service_type:%d, token_rate:%d, token_bucket_size:%d, "
-        "peak_bandwidth:%d, access_latency:%d",
-        (uint8_t)flow_direction, (uint8_t)service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
-  }
-
-  void OnFlushOccurred() override {
-    LOG_DEBUG("OnFlushOccurred");
-  }
-
-  void OnRoleDiscoveryComplete(Role current_role) override {
-    LOG_DEBUG("OnRoleDiscoveryComplete current_role:%d", (uint8_t)current_role);
-  }
-
-  void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) override {
-    LOG_DEBUG("OnReadLinkPolicySettingsComplete link_policy_settings:%d", link_policy_settings);
-  }
-
-  void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) override {
-    LOG_DEBUG("OnReadAutomaticFlushTimeoutComplete flush_timeout:%d", flush_timeout);
-  }
-
-  void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) override {
-    LOG_DEBUG("OnReadTransmitPowerLevelComplete transmit_power_level:%d", transmit_power_level);
-  }
-
-  void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) override {
-    LOG_DEBUG("OnReadLinkSupervisionTimeoutComplete link_supervision_timeout:%d", link_supervision_timeout);
-  }
-
-  void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) override {
-    LOG_DEBUG("OnReadFailedContactCounterComplete failed_contact_counter:%d", failed_contact_counter);
-  }
-
-  void OnReadLinkQualityComplete(uint8_t link_quality) override {
-    LOG_DEBUG("OnReadLinkQualityComplete link_quality:%d", link_quality);
-  }
-
-  void OnReadAfhChannelMapComplete(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) {
-    LOG_DEBUG("OnReadAfhChannelMapComplete afh_mode:%d", (uint8_t)afh_mode);
-  }
-
-  void OnReadRssiComplete(uint8_t rssi) override {
-    LOG_DEBUG("OnReadRssiComplete rssi:%d", rssi);
-  }
-
-  void OnReadClockComplete(uint32_t clock, uint16_t accuracy) override {
-    LOG_DEBUG("OnReadClockComplete clock:%d, accuracy:%d", clock, accuracy);
-  }
-
-  ::grpc::Status FetchConnectionFailed(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                       ::grpc::ServerWriter<ConnectionFailedEvent>* writer) override {
-    return pending_connection_failed_.RunLoop(context, writer);
-  };
-
-  ::grpc::Status FetchDisconnection(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                    ::grpc::ServerWriter<DisconnectionEvent>* writer) override {
-    return pending_disconnection_.RunLoop(context, writer);
-  }
-
- private:
-  AclManager* acl_manager_;
-  Controller* controller_;
-  HciLayer* hci_layer_;
-  ::bluetooth::os::Handler* facade_handler_;
-  mutable std::mutex acl_connections_mutex_;
-  std::map<std::string, std::shared_ptr<AclConnection>> acl_connections_;
-  bool fetching_acl_data_ = false;
-  ::bluetooth::grpc::GrpcEventQueue<AclData> pending_acl_data_{"FetchAclData"};
-  ::bluetooth::grpc::GrpcEventQueue<ConnectionEvent> pending_connection_complete_{"FetchConnectionComplete"};
-  ::bluetooth::grpc::GrpcEventQueue<ConnectionFailedEvent> pending_connection_failed_{"FetchConnectionFailed"};
-  ::bluetooth::grpc::GrpcEventQueue<DisconnectionEvent> pending_disconnection_{"FetchDisconnection"};
-};
-
-void AclManagerFacadeModule::ListDependencies(ModuleList* list) {
-  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
-  list->add<AclManager>();
-  list->add<Controller>();
-  list->add<HciLayer>();
-}
-
-void AclManagerFacadeModule::Start() {
-  ::bluetooth::grpc::GrpcFacadeModule::Start();
-  service_ = new AclManagerFacadeService(GetDependency<AclManager>(), GetDependency<Controller>(),
-                                         GetDependency<HciLayer>(), GetHandler());
-}
-
-void AclManagerFacadeModule::Stop() {
-  delete service_;
-  ::bluetooth::grpc::GrpcFacadeModule::Stop();
-}
-
-::grpc::Service* AclManagerFacadeModule::GetService() const {
-  return service_;
-}
-
-const ModuleFactory AclManagerFacadeModule::Factory =
-    ::bluetooth::ModuleFactory([]() { return new AclManagerFacadeModule(); });
-
-class ClassicSecurityManagerFacadeService : public ClassicSecurityManagerFacade::Service,
-                                            public ::bluetooth::hci::ClassicSecurityCommandCallbacks {
- public:
-  ClassicSecurityManagerFacadeService(ClassicSecurityManager* classic_security_manager, Controller* controller,
-                                      HciLayer* hci_layer, ::bluetooth::os::Handler* facade_handler)
-      : classic_security_manager_(classic_security_manager), facade_handler_(facade_handler) {
-    classic_security_manager_->RegisterCallbacks(this, facade_handler_);
-  }
-
-  ::grpc::Status LinkKeyRequestReply(::grpc::ServerContext* context,
-                                     const ::bluetooth::hci::LinkKeyRequestReplyMessage* request,
-                                     ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    common::LinkKey link_key;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    ASSERT(common::LinkKey::FromString(request->link_key(), link_key));
-    classic_security_manager_->LinkKeyRequestReply(peer, link_key);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status LinkKeyRequestNegativeReply(::grpc::ServerContext* context,
-                                             const ::bluetooth::facade::BluetoothAddress* request,
-                                             ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->address(), peer));
-    classic_security_manager_->LinkKeyRequestNegativeReply(peer);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status PinCodeRequestReply(::grpc::ServerContext* context,
-                                     const ::bluetooth::hci::PinCodeRequestReplyMessage* request,
-                                     ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    uint8_t len = request->len();
-    std::string pin_code = request->pin_code();
-    classic_security_manager_->PinCodeRequestReply(peer, len, pin_code);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status PinCodeRequestNegativeReply(::grpc::ServerContext* context,
-                                             const ::bluetooth::facade::BluetoothAddress* request,
-                                             ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->address(), peer));
-    classic_security_manager_->PinCodeRequestNegativeReply(peer);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status IoCapabilityRequestReply(::grpc::ServerContext* context,
-                                          const ::bluetooth::hci::IoCapabilityRequestReplyMessage* request,
-                                          ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    IoCapability io_capability = (IoCapability)request->io_capability();
-    OobDataPresent oob_present = (OobDataPresent)request->oob_present();
-    AuthenticationRequirements authentication_requirements =
-        (AuthenticationRequirements)request->authentication_requirements();
-    classic_security_manager_->IoCapabilityRequestReply(peer, io_capability, oob_present, authentication_requirements);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status IoCapabilityRequestNegativeReply(
-      ::grpc::ServerContext* context, const ::bluetooth::hci::IoCapabilityRequestNegativeReplyMessage* request,
-      ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    ErrorCode reason = (ErrorCode)request->reason();
-    classic_security_manager_->IoCapabilityRequestNegativeReply(peer, reason);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status UserConfirmationRequestReply(::grpc::ServerContext* context,
-                                              const ::bluetooth::facade::BluetoothAddress* request,
-                                              ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->address(), peer));
-    classic_security_manager_->UserConfirmationRequestReply(peer);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status UserConfirmationRequestNegativeReply(::grpc::ServerContext* context,
-                                                      const ::bluetooth::facade::BluetoothAddress* request,
-                                                      ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->address(), peer));
-    classic_security_manager_->UserConfirmationRequestNegativeReply(peer);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status UserPasskeyRequestReply(::grpc::ServerContext* context,
-                                         const ::bluetooth::hci::UserPasskeyRequestReplyMessage* request,
-                                         ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    uint32_t passkey = request->passkey();
-    classic_security_manager_->UserPasskeyRequestReply(peer, passkey);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status UserPasskeyRequestNegativeReply(::grpc::ServerContext* context,
-                                                 const ::bluetooth::facade::BluetoothAddress* request,
-                                                 ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->address(), peer));
-    classic_security_manager_->UserPasskeyRequestNegativeReply(peer);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status RemoteOobDataRequestReply(::grpc::ServerContext* context,
-                                           const ::bluetooth::hci::RemoteOobDataRequestReplyMessage* request,
-                                           ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    std::string c_string = request->c();
-    std::string r_string = request->r();
-    std::array<uint8_t, 16> c;
-    std::array<uint8_t, 16> r;
-    std::copy(std::begin(c_string), std::end(c_string), std::begin(c));
-    std::copy(std::begin(r_string), std::end(r_string), std::begin(r));
-    classic_security_manager_->RemoteOobDataRequestReply(peer, c, r);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status RemoteOobDataRequestNegativeReply(::grpc::ServerContext* context,
-                                                   const ::bluetooth::facade::BluetoothAddress* request,
-                                                   ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->address(), peer));
-    classic_security_manager_->RemoteOobDataRequestNegativeReply(peer);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status ReadStoredLinkKey(::grpc::ServerContext* context,
-                                   const ::bluetooth::hci::ReadStoredLinkKeyMessage* request,
-                                   ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    ReadStoredLinkKeyReadAllFlag read_all_flag = (ReadStoredLinkKeyReadAllFlag)request->read_all_flag();
-    classic_security_manager_->ReadStoredLinkKey(peer, read_all_flag);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status WriteStoredLinkKey(::grpc::ServerContext* context,
-                                    const ::bluetooth::hci::WriteStoredLinkKeyMessage* request,
-                                    ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    uint8_t num_keys_to_write = request->num_keys_to_write();
-    std::vector<KeyAndAddress> keys;
-    for (size_t i = 0; i < num_keys_to_write; i++) {
-      KeyAndAddress key;
-      common::LinkKey link_key;
-      ASSERT(Address::FromString(request->remote().address(), key.address_));
-      ASSERT(common::LinkKey::FromString(request->link_keys(), link_key));
-      std::copy(std::begin(link_key.link_key), std::end(link_key.link_key), std::begin(key.link_key_));
-      keys.push_back(key);
-    }
-
-    classic_security_manager_->WriteStoredLinkKey(keys);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status DeleteStoredLinkKey(::grpc::ServerContext* context,
-                                     const ::bluetooth::hci::DeleteStoredLinkKeyMessage* request,
-                                     ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    DeleteStoredLinkKeyDeleteAllFlag delete_all_flag = (DeleteStoredLinkKeyDeleteAllFlag)request->delete_all_flag();
-    classic_security_manager_->DeleteStoredLinkKey(peer, delete_all_flag);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status RefreshEncryptionKey(::grpc::ServerContext* context,
-                                      const ::bluetooth::hci::RefreshEncryptionKeyMessage* request,
-                                      ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    classic_security_manager_->RefreshEncryptionKey(request->connection_handle());
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status ReadSimplePairingMode(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                       ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    classic_security_manager_->ReadSimplePairingMode();
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status WriteSimplePairingMode(::grpc::ServerContext* context,
-                                        const ::bluetooth::hci::WriteSimplePairingModeMessage* request,
-                                        ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Enable simple_pairing_mode = (Enable)request->simple_pairing_mode();
-    classic_security_manager_->WriteSimplePairingMode(simple_pairing_mode);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status ReadLocalOobData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                  ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    classic_security_manager_->ReadLocalOobData();
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status SendKeypressNotification(::grpc::ServerContext* context,
-                                          const ::bluetooth::hci::SendKeypressNotificationMessage* request,
-                                          ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    Address peer;
-    ASSERT(Address::FromString(request->remote().address(), peer));
-    KeypressNotificationType notification_type = (KeypressNotificationType)request->notification_type();
-    classic_security_manager_->SendKeypressNotification(peer, notification_type);
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status ReadLocalOobExtendedData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                          ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    classic_security_manager_->ReadLocalOobExtendedData();
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status ReadEncryptionKeySize(::grpc::ServerContext* context,
-                                       const ::bluetooth::hci::ReadEncryptionKeySizeMessage* request,
-                                       ::google::protobuf::Empty* response) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    classic_security_manager_->ReadEncryptionKeySize(request->connection_handle());
-    return ::grpc::Status::OK;
-  };
-
-  ::grpc::Status FetchCommandCompleteEvent(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                                           ::grpc::ServerWriter<CommandCompleteEvent>* writer) override {
-    return pending_connection_complete_.RunLoop(context, writer);
-  };
-
-  void OnCommandComplete(CommandCompleteView status) override {
-    CommandCompleteEvent response;
-    response.set_command_opcode(static_cast<uint32_t>(status.GetCommandOpCode()));
-    pending_connection_complete_.OnIncomingEvent(response);
-  }
-
- private:
-  ClassicSecurityManager* classic_security_manager_;
-  mutable std::mutex mutex_;
-  ::bluetooth::os::Handler* facade_handler_;
-  ::bluetooth::grpc::GrpcEventQueue<CommandCompleteEvent> pending_connection_complete_{"FetchCommandCompleteEvent"};
-};
-
-void ClassicSecurityManagerFacadeModule::ListDependencies(ModuleList* list) {
-  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
-  list->add<ClassicSecurityManager>();
-  list->add<Controller>();
-  list->add<HciLayer>();
-}
-
-void ClassicSecurityManagerFacadeModule::Start() {
-  ::bluetooth::grpc::GrpcFacadeModule::Start();
-  service_ = new ClassicSecurityManagerFacadeService(
-      GetDependency<ClassicSecurityManager>(), GetDependency<Controller>(), GetDependency<HciLayer>(), GetHandler());
-}
-
-void ClassicSecurityManagerFacadeModule::Stop() {
-  delete service_;
-  ::bluetooth::grpc::GrpcFacadeModule::Stop();
-}
-
-::grpc::Service* ClassicSecurityManagerFacadeModule::GetService() const {
-  return service_;
-}
-
-const ModuleFactory ClassicSecurityManagerFacadeModule::Factory =
-    ::bluetooth::ModuleFactory([]() { return new ClassicSecurityManagerFacadeModule(); });
-
-}  // namespace hci
-}  // namespace bluetooth
diff --git a/gd/hci/facade.h b/gd/hci/facade.h
deleted file mode 100644
index b38286d..0000000
--- a/gd/hci/facade.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <grpc++/grpc++.h>
-
-#include "grpc/grpc_module.h"
-#include "hci/acl_manager.h"
-
-namespace bluetooth {
-namespace hci {
-
-class AclManagerFacadeService;
-
-class AclManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
- public:
-  static const ModuleFactory Factory;
-
-  void ListDependencies(ModuleList* list) override;
-  void Start() override;
-  void Stop() override;
-  ::grpc::Service* GetService() const override;
-
- private:
-  AclManagerFacadeService* service_;
-};
-
-class ClassicSecurityManagerFacadeService;
-
-class ClassicSecurityManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
- public:
-  static const ModuleFactory Factory;
-
-  void ListDependencies(ModuleList* list) override;
-  void Start() override;
-  void Stop() override;
-  ::grpc::Service* GetService() const override;
-
- private:
-  ClassicSecurityManagerFacadeService* service_;
-};
-
-}  // namespace hci
-}  // namespace bluetooth
diff --git a/gd/hci/facade.proto b/gd/hci/facade.proto
deleted file mode 100644
index 1e7769c..0000000
--- a/gd/hci/facade.proto
+++ /dev/null
@@ -1,158 +0,0 @@
-syntax = "proto3";
-
-package bluetooth.hci;
-
-import "google/protobuf/empty.proto";
-import "facade/common.proto";
-
-service AclManagerFacade {
-  rpc SetPageScanMode(PageScanMode) returns (google.protobuf.Empty) {}
-  rpc Connect(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc Disconnect(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc AuthenticationRequested(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc FetchConnectionComplete(google.protobuf.Empty) returns (stream ConnectionEvent) {}
-  rpc FetchDisconnection(google.protobuf.Empty) returns (stream DisconnectionEvent) {}
-  rpc FetchConnectionFailed(google.protobuf.Empty) returns (stream ConnectionFailedEvent) {}
-  rpc SendAclData(AclData) returns (google.protobuf.Empty) {}
-  rpc FetchAclData(google.protobuf.Empty) returns (stream AclData) {}
-  rpc TestInternalHciCommands(google.protobuf.Empty) returns (google.protobuf.Empty) {}
-  rpc TestInternalHciLeCommands(google.protobuf.Empty) returns (google.protobuf.Empty) {}
-  rpc TestClassicConnectionManagementCommands(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-}
-
-message PageScanMode {
-  bool enabled = 1;
-}
-
-message ConnectionEvent {
-  facade.BluetoothAddress remote = 1;
-  uint32 connection_handle = 2;
-}
-
-message DisconnectionEvent {
-  facade.BluetoothAddress remote = 1;
-  uint32 reason = 2;
-}
-
-message ConnectionFailedEvent {
-  facade.BluetoothAddress remote = 1;
-  uint32 reason = 2;
-}
-
-message AclData {
-  facade.BluetoothAddress remote = 1;
-  bytes payload = 2;
-}
-
-service ClassicPairingFacade {
-  rpc SetPairingMode(PairingMode) returns (google.protobuf.Empty) {}
-  rpc DeletePairing(DeletePairingRequest) returns (google.protobuf.Empty) {}
-}
-
-message PairingMode {
-  bool enabled = 1;
-}
-
-message DeletePairingRequest {
-  bool deleteAll = 1;
-  facade.BluetoothAddress remote = 2;
-}
-
-service ClassicSecurityManagerFacade {
-  rpc LinkKeyRequestReply(LinkKeyRequestReplyMessage) returns (google.protobuf.Empty) {}
-  rpc LinkKeyRequestNegativeReply(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc PinCodeRequestReply(PinCodeRequestReplyMessage) returns (google.protobuf.Empty) {}
-  rpc PinCodeRequestNegativeReply(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc IoCapabilityRequestReply(IoCapabilityRequestReplyMessage) returns (google.protobuf.Empty) {}
-  rpc IoCapabilityRequestNegativeReply(IoCapabilityRequestNegativeReplyMessage) returns (google.protobuf.Empty) {}
-  rpc UserConfirmationRequestReply(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc UserConfirmationRequestNegativeReply(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc UserPasskeyRequestReply(UserPasskeyRequestReplyMessage) returns (google.protobuf.Empty) {}
-  rpc UserPasskeyRequestNegativeReply(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc RemoteOobDataRequestReply(RemoteOobDataRequestReplyMessage) returns (google.protobuf.Empty) {}
-  rpc RemoteOobDataRequestNegativeReply(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
-  rpc ReadStoredLinkKey(ReadStoredLinkKeyMessage) returns (google.protobuf.Empty) {}
-  rpc WriteStoredLinkKey(WriteStoredLinkKeyMessage) returns (google.protobuf.Empty) {}
-  rpc DeleteStoredLinkKey(DeleteStoredLinkKeyMessage) returns (google.protobuf.Empty) {}
-  rpc RefreshEncryptionKey(RefreshEncryptionKeyMessage) returns (google.protobuf.Empty) {}
-  rpc ReadSimplePairingMode(google.protobuf.Empty) returns (google.protobuf.Empty) {}
-  rpc WriteSimplePairingMode(WriteSimplePairingModeMessage) returns (google.protobuf.Empty) {}
-  rpc ReadLocalOobData(google.protobuf.Empty) returns (google.protobuf.Empty) {}
-  rpc SendKeypressNotification(SendKeypressNotificationMessage) returns (google.protobuf.Empty) {}
-  rpc ReadLocalOobExtendedData(google.protobuf.Empty) returns (google.protobuf.Empty) {}
-  rpc ReadEncryptionKeySize(ReadEncryptionKeySizeMessage) returns (google.protobuf.Empty) {}
-
-  rpc FetchCommandCompleteEvent(google.protobuf.Empty) returns (stream CommandCompleteEvent) {}
-}
-
-
-message CommandCompleteEvent {
-  uint32 command_opcode = 1;
-}
-
-message LinkKeyRequestReplyMessage {
-  facade.BluetoothAddress remote = 1;
-  bytes link_key = 2;
-}
-
-message PinCodeRequestReplyMessage {
-  facade.BluetoothAddress remote = 1;
-  uint32 len = 2;
-  bytes pin_code = 3;
-}
-
-message IoCapabilityRequestReplyMessage {
-  facade.BluetoothAddress remote = 1;
-  uint32 io_capability = 2;
-  uint32 oob_present = 3;
-  uint32 authentication_requirements = 4;
-}
-
-message IoCapabilityRequestNegativeReplyMessage {
-  facade.BluetoothAddress remote = 1;
-  uint32 reason = 2;
-}
-
-message UserPasskeyRequestReplyMessage {
-  facade.BluetoothAddress remote = 1;
-  uint32 passkey = 2;
-}
-
-message RemoteOobDataRequestReplyMessage {
-  facade.BluetoothAddress remote = 1;
-  bytes c = 2;
-  bytes r = 3;
-}
-
-message ReadStoredLinkKeyMessage {
-  facade.BluetoothAddress remote = 1;
-  uint32 read_all_flag = 2;
-}
-
-message WriteStoredLinkKeyMessage {
-  uint32 num_keys_to_write = 1;
-  facade.BluetoothAddress remote = 2;
-  bytes link_keys = 3;
-}
-
-message DeleteStoredLinkKeyMessage {
-  facade.BluetoothAddress remote = 1;
-  uint32 delete_all_flag = 2;
-}
-
-message RefreshEncryptionKeyMessage {
-  uint32 connection_handle = 1;
-}
-
-message WriteSimplePairingModeMessage {
-  uint32 simple_pairing_mode = 1;
-}
-
-message SendKeypressNotificationMessage {
-  facade.BluetoothAddress remote = 1;
-  uint32 notification_type = 2;
-}
-
-message ReadEncryptionKeySizeMessage {
-  uint32 connection_handle = 1;
-}
\ No newline at end of file
diff --git a/gd/hci/facade/acl_manager_facade.cc b/gd/hci/facade/acl_manager_facade.cc
new file mode 100644
index 0000000..e343f93
--- /dev/null
+++ b/gd/hci/facade/acl_manager_facade.cc
@@ -0,0 +1,537 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/facade/acl_manager_facade.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/acl_manager.h"
+#include "hci/facade/acl_manager_facade.grpc.pb.h"
+#include "hci/facade/acl_manager_facade.pb.h"
+#include "hci/hci_packets.h"
+#include "packet/raw_builder.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+using ::bluetooth::packet::RawBuilder;
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+using acl_manager::ClassicAclConnection;
+using acl_manager::ConnectionCallbacks;
+using acl_manager::ConnectionManagementCallbacks;
+
+class AclManagerFacadeService : public AclManagerFacade::Service, public ConnectionCallbacks {
+ public:
+  AclManagerFacadeService(AclManager* acl_manager, ::bluetooth::os::Handler* facade_handler)
+      : acl_manager_(acl_manager), facade_handler_(facade_handler) {
+    acl_manager_->RegisterCallbacks(this, facade_handler_);
+  }
+
+  ~AclManagerFacadeService() override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    for (auto& connection : acl_connections_) {
+      connection.second.connection_->GetAclQueueEnd()->UnregisterDequeue();
+    }
+  }
+
+  ::grpc::Status CreateConnection(
+      ::grpc::ServerContext* context,
+      const ConnectionMsg* request,
+      ::grpc::ServerWriter<ConnectionEvent>* writer) override {
+    Address peer;
+    ASSERT(Address::FromString(request->address(), peer));
+    acl_manager_->CreateConnection(peer);
+    if (per_connection_events_.size() > current_connection_request_) {
+      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding request is supported");
+    }
+    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>>(
+        std::string("connection attempt ") + std::to_string(current_connection_request_)));
+    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+  }
+
+  ::grpc::Status Disconnect(
+      ::grpc::ServerContext* context, const HandleMsg* request, ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto connection = acl_connections_.find(request->handle());
+    if (connection == acl_connections_.end()) {
+      LOG_ERROR("Invalid handle");
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+    } else {
+      connection->second.connection_->Disconnect(DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
+      return ::grpc::Status::OK;
+    }
+  }
+
+  ::grpc::Status AuthenticationRequested(
+      ::grpc::ServerContext* context, const HandleMsg* request, ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto connection = acl_connections_.find(request->handle());
+    if (connection == acl_connections_.end()) {
+      LOG_ERROR("Invalid handle");
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+    } else {
+      connection->second.connection_->AuthenticationRequested();
+      return ::grpc::Status::OK;
+    }
+  };
+
+#define GET_CONNECTION(view)                                                         \
+  std::map<uint16_t, Connection>::iterator connection;                               \
+  do {                                                                               \
+    if (!view.IsValid()) {                                                           \
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \
+    }                                                                                \
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);                       \
+    connection = acl_connections_.find(view.GetConnectionHandle());                  \
+    if (connection == acl_connections_.end()) {                                      \
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \
+    }                                                                                \
+  } while (0)
+
+  ::grpc::Status ConnectionCommand(
+      ::grpc::ServerContext* context,
+      const ConnectionCommandMsg* request,
+      ::google::protobuf::Empty* response) override {
+    auto command_view = ConnectionManagementCommandView::Create(CommandPacketView::Create(
+        std::make_shared<std::vector<uint8_t>>(request->packet().begin(), request->packet().end())));
+    if (!command_view.IsValid()) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet");
+    }
+    switch (command_view.GetOpCode()) {
+      case OpCode::AUTHENTICATION_REQUESTED: {
+        GET_CONNECTION(AuthenticationRequestedView::Create(command_view));
+        connection->second.connection_->AuthenticationRequested();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::DISCONNECT: {
+        auto view = DisconnectView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->Disconnect(view.GetReason());
+        return ::grpc::Status::OK;
+      }
+      case OpCode::CHANGE_CONNECTION_PACKET_TYPE: {
+        auto view = ChangeConnectionPacketTypeView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->ChangeConnectionPacketType(view.GetPacketType());
+        return ::grpc::Status::OK;
+      }
+      case OpCode::SET_CONNECTION_ENCRYPTION: {
+        auto view = SetConnectionEncryptionView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->SetConnectionEncryption(view.GetEncryptionEnable());
+        return ::grpc::Status::OK;
+      }
+      case OpCode::CHANGE_CONNECTION_LINK_KEY: {
+        GET_CONNECTION(ChangeConnectionLinkKeyView::Create(command_view));
+        connection->second.connection_->ChangeConnectionLinkKey();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_CLOCK_OFFSET: {
+        GET_CONNECTION(ReadClockOffsetView::Create(command_view));
+        connection->second.connection_->ReadClockOffset();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::HOLD_MODE: {
+        auto view = HoldModeView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->HoldMode(view.GetHoldModeMaxInterval(), view.GetHoldModeMinInterval());
+        return ::grpc::Status::OK;
+      }
+      case OpCode::SNIFF_MODE: {
+        auto view = SniffModeView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->SniffMode(
+            view.GetSniffMaxInterval(), view.GetSniffMinInterval(), view.GetSniffAttempt(), view.GetSniffTimeout());
+        return ::grpc::Status::OK;
+      }
+      case OpCode::EXIT_SNIFF_MODE: {
+        GET_CONNECTION(ExitSniffModeView::Create(command_view));
+        connection->second.connection_->ExitSniffMode();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::FLUSH: {
+        GET_CONNECTION(FlushView::Create(command_view));
+        connection->second.connection_->Flush();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_AUTOMATIC_FLUSH_TIMEOUT: {
+        GET_CONNECTION(ReadAutomaticFlushTimeoutView::Create(command_view));
+        connection->second.connection_->ReadAutomaticFlushTimeout();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::WRITE_AUTOMATIC_FLUSH_TIMEOUT: {
+        auto view = WriteAutomaticFlushTimeoutView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->WriteAutomaticFlushTimeout(view.GetFlushTimeout());
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_TRANSMIT_POWER_LEVEL: {
+        auto view = ReadTransmitPowerLevelView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->ReadTransmitPowerLevel(view.GetType());
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_LINK_SUPERVISION_TIMEOUT: {
+        GET_CONNECTION(ReadLinkSupervisionTimeoutView::Create(command_view));
+        connection->second.connection_->ReadLinkSupervisionTimeout();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::WRITE_LINK_SUPERVISION_TIMEOUT: {
+        auto view = WriteLinkSupervisionTimeoutView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->WriteLinkSupervisionTimeout(view.GetLinkSupervisionTimeout());
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_FAILED_CONTACT_COUNTER: {
+        GET_CONNECTION(ReadFailedContactCounterView::Create(command_view));
+        connection->second.connection_->ReadFailedContactCounter();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::RESET_FAILED_CONTACT_COUNTER: {
+        GET_CONNECTION(ResetFailedContactCounterView::Create(command_view));
+        connection->second.connection_->ResetFailedContactCounter();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_LINK_QUALITY: {
+        GET_CONNECTION(ReadLinkQualityView::Create(command_view));
+        connection->second.connection_->ReadLinkQuality();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_AFH_CHANNEL_MAP: {
+        GET_CONNECTION(ReadAfhChannelMapView::Create(command_view));
+        connection->second.connection_->ReadAfhChannelMap();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_RSSI: {
+        GET_CONNECTION(ReadRssiView::Create(command_view));
+        connection->second.connection_->ReadRssi();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_CLOCK: {
+        auto view = ReadClockView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->ReadClock(view.GetWhichClock());
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_REMOTE_VERSION_INFORMATION: {
+        GET_CONNECTION(ReadRemoteVersionInformationView::Create(command_view));
+        connection->second.connection_->ReadRemoteVersionInformation();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_REMOTE_SUPPORTED_FEATURES: {
+        GET_CONNECTION(ReadRemoteSupportedFeaturesView::Create(command_view));
+        connection->second.connection_->ReadRemoteSupportedFeatures();
+        return ::grpc::Status::OK;
+      }
+      case OpCode::READ_REMOTE_EXTENDED_FEATURES: {
+        GET_CONNECTION(ReadRemoteExtendedFeaturesView::Create(command_view));
+        connection->second.connection_->ReadRemoteExtendedFeatures();
+        return ::grpc::Status::OK;
+      }
+      default:
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet");
+    }
+  }
+#undef GET_CONNECTION
+
+  ::grpc::Status FetchIncomingConnection(
+      ::grpc::ServerContext* context,
+      const google::protobuf::Empty* request,
+      ::grpc::ServerWriter<ConnectionEvent>* writer) override {
+    if (per_connection_events_.size() > current_connection_request_) {
+      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding connection is supported");
+    }
+    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>>(
+        std::string("incoming connection ") + std::to_string(current_connection_request_)));
+    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+  }
+
+  ::grpc::Status SendAclData(
+      ::grpc::ServerContext* context, const AclData* request, ::google::protobuf::Empty* response) override {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    {
+      std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+      auto connection = acl_connections_.find(request->handle());
+      if (connection == acl_connections_.end()) {
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+      }
+      // TODO: This is unsafe because connection may have gone
+      connection->second.connection_->GetAclQueueEnd()->RegisterEnqueue(
+          facade_handler_,
+          common::Bind(
+              &AclManagerFacadeService::enqueue_packet,
+              common::Unretained(this),
+              common::Unretained(request),
+              common::Passed(std::move(promise))));
+      auto status = future.wait_for(std::chrono::milliseconds(1000));
+      if (status != std::future_status::ready) {
+        return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Can't send packet");
+      }
+    }
+    return ::grpc::Status::OK;
+  }
+
+  std::unique_ptr<BasePacketBuilder> enqueue_packet(const AclData* request, std::promise<void> promise) {
+    auto connection = acl_connections_.find(request->handle());
+    ASSERT_LOG(connection != acl_connections_.end(), "handle %d", request->handle());
+    connection->second.connection_->GetAclQueueEnd()->UnregisterEnqueue();
+    std::unique_ptr<RawBuilder> packet =
+        std::make_unique<RawBuilder>(std::vector<uint8_t>(request->payload().begin(), request->payload().end()));
+    promise.set_value();
+    return packet;
+  }
+
+  ::grpc::Status FetchAclData(
+      ::grpc::ServerContext* context, const HandleMsg* request, ::grpc::ServerWriter<AclData>* writer) override {
+    auto connection = acl_connections_.find(request->handle());
+    if (connection == acl_connections_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+    }
+    return connection->second.pending_acl_data_.RunLoop(context, writer);
+  }
+
+  static inline uint16_t to_handle(uint32_t current_request) {
+    return (current_request + 0x10) % 0xe00;
+  }
+
+  static inline std::string builder_to_string(std::unique_ptr<BasePacketBuilder> builder) {
+    std::vector<uint8_t> bytes;
+    BitInserter bit_inserter(bytes);
+    builder->Serialize(bit_inserter);
+    return std::string(bytes.begin(), bytes.end());
+  }
+
+  void on_incoming_acl(std::shared_ptr<ClassicAclConnection> connection, uint16_t handle) {
+    auto packet = connection->GetAclQueueEnd()->TryDequeue();
+    auto connection_tracker = acl_connections_.find(handle);
+    ASSERT_LOG(connection_tracker != acl_connections_.end(), "handle %d", handle);
+    AclData acl_data;
+    acl_data.set_handle(handle);
+    acl_data.set_payload(std::string(packet->begin(), packet->end()));
+    connection_tracker->second.pending_acl_data_.OnIncomingEvent(acl_data);
+  }
+
+  void OnConnectSuccess(std::unique_ptr<ClassicAclConnection> connection) override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    std::shared_ptr<ClassicAclConnection> shared_connection = std::move(connection);
+    uint16_t handle = to_handle(current_connection_request_);
+    acl_connections_.emplace(
+        std::piecewise_construct,
+        std::forward_as_tuple(handle),
+        std::forward_as_tuple(handle, shared_connection, per_connection_events_[current_connection_request_]));
+    shared_connection->GetAclQueueEnd()->RegisterDequeue(
+        facade_handler_,
+        common::Bind(&AclManagerFacadeService::on_incoming_acl, common::Unretained(this), shared_connection, handle));
+    auto callbacks = acl_connections_.find(handle)->second.GetCallbacks();
+    shared_connection->RegisterCallbacks(callbacks, facade_handler_);
+    auto addr = shared_connection->GetAddress();
+    std::unique_ptr<BasePacketBuilder> builder =
+        ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, addr, LinkType::ACL, Enable::DISABLED);
+    ConnectionEvent success;
+    success.set_event(builder_to_string(std::move(builder)));
+    per_connection_events_[current_connection_request_]->OnIncomingEvent(success);
+    current_connection_request_++;
+  }
+
+  void OnConnectFail(Address address, ErrorCode reason) override {
+    std::unique_ptr<BasePacketBuilder> builder =
+        ConnectionCompleteBuilder::Create(reason, 0, address, LinkType::ACL, Enable::DISABLED);
+    ConnectionEvent fail;
+    fail.set_event(builder_to_string(std::move(builder)));
+    per_connection_events_[current_connection_request_]->OnIncomingEvent(fail);
+    current_connection_request_++;
+  }
+
+  class Connection : public ConnectionManagementCallbacks {
+   public:
+    Connection(
+        uint16_t handle,
+        std::shared_ptr<ClassicAclConnection> connection,
+        std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>> event_stream)
+        : handle_(handle), connection_(std::move(connection)), event_stream_(std::move(event_stream)) {}
+
+    ConnectionManagementCallbacks* GetCallbacks() {
+      return this;
+    }
+
+    void OnMasterLinkKeyComplete(KeyFlag key_flag) override {
+      LOG_DEBUG("key_flag:%s", KeyFlagText(key_flag).c_str());
+    }
+
+    void OnRoleChange(Role new_role) override {
+      LOG_DEBUG("new_role:%d", (uint8_t)new_role);
+    }
+
+    void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) override {
+      LOG_DEBUG("link_policy_settings:%d", link_policy_settings);
+    }
+
+    void OnConnectionPacketTypeChanged(uint16_t packet_type) override {
+      LOG_DEBUG("OnConnectionPacketTypeChanged packet_type:%d", packet_type);
+    }
+
+    void OnAuthenticationComplete() override {
+      LOG_DEBUG("OnAuthenticationComplete");
+    }
+
+    void OnEncryptionChange(EncryptionEnabled enabled) override {
+      LOG_DEBUG("OnConnectionPacketTypeChanged enabled:%d", (uint8_t)enabled);
+    }
+
+    void OnChangeConnectionLinkKeyComplete() override {
+      LOG_DEBUG("OnChangeConnectionLinkKeyComplete");
+    };
+
+    void OnReadClockOffsetComplete(uint16_t clock_offset) override {
+      LOG_DEBUG("OnReadClockOffsetComplete clock_offset:%d", clock_offset);
+    };
+
+    void OnModeChange(Mode current_mode, uint16_t interval) override {
+      LOG_DEBUG("OnModeChange Mode:%d, interval:%d", (uint8_t)current_mode, interval);
+    };
+
+    void OnQosSetupComplete(
+        ServiceType service_type,
+        uint32_t token_rate,
+        uint32_t peak_bandwidth,
+        uint32_t latency,
+        uint32_t delay_variation) override {
+      LOG_DEBUG(
+          "OnQosSetupComplete service_type:%d, token_rate:%d, peak_bandwidth:%d, latency:%d, delay_variation:%d",
+          (uint8_t)service_type,
+          token_rate,
+          peak_bandwidth,
+          latency,
+          delay_variation);
+    }
+
+    void OnFlowSpecificationComplete(
+        FlowDirection flow_direction,
+        ServiceType service_type,
+        uint32_t token_rate,
+        uint32_t token_bucket_size,
+        uint32_t peak_bandwidth,
+        uint32_t access_latency) override {
+      LOG_DEBUG(
+          "OnFlowSpecificationComplete flow_direction:%d. service_type:%d, token_rate:%d, token_bucket_size:%d, "
+          "peak_bandwidth:%d, access_latency:%d",
+          (uint8_t)flow_direction,
+          (uint8_t)service_type,
+          token_rate,
+          token_bucket_size,
+          peak_bandwidth,
+          access_latency);
+    }
+
+    void OnFlushOccurred() override {
+      LOG_DEBUG("OnFlushOccurred");
+    }
+
+    void OnRoleDiscoveryComplete(Role current_role) override {
+      LOG_DEBUG("OnRoleDiscoveryComplete current_role:%d", (uint8_t)current_role);
+    }
+
+    void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) override {
+      LOG_DEBUG("OnReadAutomaticFlushTimeoutComplete flush_timeout:%d", flush_timeout);
+    }
+
+    void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) override {
+      LOG_DEBUG("OnReadTransmitPowerLevelComplete transmit_power_level:%d", transmit_power_level);
+    }
+
+    void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) override {
+      LOG_DEBUG("OnReadLinkSupervisionTimeoutComplete link_supervision_timeout:%d", link_supervision_timeout);
+    }
+
+    void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) override {
+      LOG_DEBUG("OnReadFailedContactCounterComplete failed_contact_counter:%d", failed_contact_counter);
+    }
+
+    void OnReadLinkQualityComplete(uint8_t link_quality) override {
+      LOG_DEBUG("OnReadLinkQualityComplete link_quality:%d", link_quality);
+    }
+
+    void OnReadAfhChannelMapComplete(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) override {
+      LOG_DEBUG("OnReadAfhChannelMapComplete afh_mode:%d", (uint8_t)afh_mode);
+    }
+
+    void OnReadRssiComplete(uint8_t rssi) override {
+      LOG_DEBUG("OnReadRssiComplete rssi:%d", rssi);
+    }
+
+    void OnReadClockComplete(uint32_t clock, uint16_t accuracy) override {
+      LOG_DEBUG("OnReadClockComplete clock:%d, accuracy:%d", clock, accuracy);
+    }
+
+    void OnDisconnection(ErrorCode reason) override {
+      LOG_DEBUG("OnDisconnection reason: %s", ErrorCodeText(reason).c_str());
+      std::unique_ptr<BasePacketBuilder> builder =
+          DisconnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, reason);
+      ConnectionEvent disconnection;
+      disconnection.set_event(builder_to_string(std::move(builder)));
+      event_stream_->OnIncomingEvent(disconnection);
+    }
+    uint16_t handle_;
+    std::shared_ptr<ClassicAclConnection> connection_;
+    std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>> event_stream_;
+    ::bluetooth::grpc::GrpcEventQueue<AclData> pending_acl_data_{std::string("PendingAclData") +
+                                                                 std::to_string(handle_)};
+  };
+
+ private:
+  AclManager* acl_manager_;
+  ::bluetooth::os::Handler* facade_handler_;
+  mutable std::mutex acl_connections_mutex_;
+  std::map<uint16_t, Connection> acl_connections_;
+  std::vector<std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>>> per_connection_events_;
+  uint32_t current_connection_request_{0};
+};
+
+void AclManagerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<AclManager>();
+}
+
+void AclManagerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new AclManagerFacadeService(GetDependency<AclManager>(), GetHandler());
+}
+
+void AclManagerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* AclManagerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory AclManagerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new AclManagerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/cert/cert.h b/gd/hci/facade/acl_manager_facade.h
similarity index 83%
copy from gd/hci/cert/cert.h
copy to gd/hci/facade/acl_manager_facade.h
index e3ecf46..9537349 100644
--- a/gd/hci/cert/cert.h
+++ b/gd/hci/facade/acl_manager_facade.h
@@ -23,11 +23,11 @@
 
 namespace bluetooth {
 namespace hci {
-namespace cert {
+namespace facade {
 
-class AclManagerCertService;
+class AclManagerFacadeService;
 
-class AclManagerCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class AclManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
@@ -37,9 +37,9 @@
   ::grpc::Service* GetService() const override;
 
  private:
-  AclManagerCertService* service_;
+  AclManagerFacadeService* service_;
 };
 
-}  // namespace cert
+}  // namespace facade
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/facade/acl_manager_facade.proto b/gd/hci/facade/acl_manager_facade.proto
new file mode 100644
index 0000000..bd532a4
--- /dev/null
+++ b/gd/hci/facade/acl_manager_facade.proto
@@ -0,0 +1,53 @@
+syntax = "proto3";
+
+package bluetooth.hci;
+
+import "google/protobuf/empty.proto";
+
+service AclManagerFacade {
+  rpc CreateConnection(ConnectionMsg) returns (stream ConnectionEvent) {}
+  rpc CancelConnection(ConnectionMsg) returns (google.protobuf.Empty) {}
+  rpc Disconnect(HandleMsg) returns (google.protobuf.Empty) {}
+  rpc WriteDefaultLinkPolicySettings(PolicyMsg) returns (google.protobuf.Empty) {}
+  rpc AuthenticationRequested(HandleMsg) returns (google.protobuf.Empty) {}
+  rpc ConnectionCommand(ConnectionCommandMsg) returns (google.protobuf.Empty) {}
+  rpc SwitchRole(RoleMsg) returns (google.protobuf.Empty) {}
+  rpc SendAclData(AclData) returns (google.protobuf.Empty) {}
+  rpc FetchAclData(HandleMsg) returns (stream AclData) {}
+  rpc FetchIncomingConnection(google.protobuf.Empty) returns (stream ConnectionEvent) {}
+}
+
+message HandleMsg {
+  uint32 handle = 1;
+}
+
+message ConnectionMsg {
+  bytes address = 1;
+}
+
+message PolicyMsg {
+  uint32 policy = 1;
+}
+
+enum NewRole {
+  MASTER = 0;
+  SLAVE = 1;
+}
+
+message RoleMsg {
+  bytes address = 1;
+  NewRole role = 2;
+}
+
+message ConnectionCommandMsg {
+  bytes packet = 1;
+}
+
+message ConnectionEvent {
+  bytes event = 1;
+}
+
+message AclData {
+  uint32 handle = 1;
+  bytes payload = 2;
+}
diff --git a/gd/hci/facade/controller_facade.cc b/gd/hci/facade/controller_facade.cc
new file mode 100644
index 0000000..c65dff0
--- /dev/null
+++ b/gd/hci/facade/controller_facade.cc
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#include "hci/facade/controller_facade.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/bind.h"
+#include "common/blocking_queue.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/address.h"
+#include "hci/controller.h"
+#include "hci/facade/controller_facade.grpc.pb.h"
+#include "hci/facade/controller_facade.pb.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class ControllerFacadeService : public ControllerFacade::Service {
+ public:
+  ControllerFacadeService(Controller* controller, ::bluetooth::os::Handler*) : controller_(controller) {}
+
+  ::grpc::Status GetMacAddress(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                               AddressMsg* response) override {
+    Address local_address = controller_->GetControllerMacAddress();
+    response->set_address(local_address.ToString());
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status GetLocalName(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                              NameMsg* response) override {
+    std::string local_name = controller_->GetControllerLocalName();
+    response->set_name(local_name);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status WriteLocalName(::grpc::ServerContext* context, const NameMsg* request,
+                                ::google::protobuf::Empty* response) override {
+    controller_->WriteLocalName(request->name());
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status GetLocalExtendedFeatures(::grpc::ServerContext* context, const PageNumberMsg* request,
+                                          FeaturesMsg* response) override {
+    if (request->page_number() > controller_->GetControllerLocalExtendedFeaturesMaxPageNumber()) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Local Extended Features page number out of range");
+    }
+    response->set_page(controller_->GetControllerLocalExtendedFeatures(request->page_number()));
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  Controller* controller_;
+};
+
+void ControllerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<Controller>();
+}
+
+void ControllerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new ControllerFacadeService(GetDependency<Controller>(), GetHandler());
+}
+
+void ControllerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* ControllerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory ControllerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new ControllerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/cert/cert.h b/gd/hci/facade/controller_facade.h
similarity index 77%
copy from gd/hci/cert/cert.h
copy to gd/hci/facade/controller_facade.h
index e3ecf46..c1f0f18 100644
--- a/gd/hci/cert/cert.h
+++ b/gd/hci/facade/controller_facade.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -19,15 +19,15 @@
 #include <grpc++/grpc++.h>
 
 #include "grpc/grpc_module.h"
-#include "hci/acl_manager.h"
+#include "hci/controller.h"
 
 namespace bluetooth {
 namespace hci {
-namespace cert {
+namespace facade {
 
-class AclManagerCertService;
+class ControllerFacadeService;
 
-class AclManagerCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class ControllerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
@@ -37,9 +37,9 @@
   ::grpc::Service* GetService() const override;
 
  private:
-  AclManagerCertService* service_;
+  ControllerFacadeService* service_;
 };
 
-}  // namespace cert
+}  // namespace facade
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/facade/controller_facade.proto b/gd/hci/facade/controller_facade.proto
new file mode 100644
index 0000000..edf9de7
--- /dev/null
+++ b/gd/hci/facade/controller_facade.proto
@@ -0,0 +1,28 @@
+syntax = "proto3";
+
+package bluetooth.hci;
+
+import "google/protobuf/empty.proto";
+
+service ControllerFacade {
+  rpc GetMacAddress(google.protobuf.Empty) returns (AddressMsg) {}
+  rpc WriteLocalName(NameMsg) returns (google.protobuf.Empty) {}
+  rpc GetLocalName(google.protobuf.Empty) returns (NameMsg) {}
+  rpc GetLocalExtendedFeatures(PageNumberMsg) returns (FeaturesMsg) {}
+}
+
+message AddressMsg {
+  bytes address = 1;
+}
+
+message NameMsg {
+  bytes name = 1;
+}
+
+message PageNumberMsg {
+  uint32 page_number = 1;
+}
+
+message FeaturesMsg {
+  uint64 page = 1;
+}
diff --git a/gd/hci/facade/facade.cc b/gd/hci/facade/facade.cc
new file mode 100644
index 0000000..15248bf
--- /dev/null
+++ b/gd/hci/facade/facade.cc
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/facade/facade.h"
+
+#include <memory>
+
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/controller.h"
+#include "hci/facade/facade.grpc.pb.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class HciLayerFacadeService : public HciLayerFacade::Service {
+ public:
+  HciLayerFacadeService(HciLayer* hci_layer, Controller* controller, ::bluetooth::os::Handler* facade_handler)
+      : hci_layer_(hci_layer), controller_(controller), facade_handler_(facade_handler) {}
+
+  virtual ~HciLayerFacadeService() {
+    if (unregister_acl_dequeue_) {
+      hci_layer_->GetAclQueueEnd()->UnregisterDequeue();
+    }
+    if (waiting_acl_packet_ != nullptr) {
+      hci_layer_->GetAclQueueEnd()->UnregisterEnqueue();
+      if (waiting_acl_packet_ != nullptr) {
+        waiting_acl_packet_.reset();
+      }
+    }
+  }
+
+  class TestCommandBuilder : public CommandPacketBuilder {
+   public:
+    explicit TestCommandBuilder(std::vector<uint8_t> bytes)
+        : CommandPacketBuilder(OpCode::NONE), bytes_(std::move(bytes)) {}
+    size_t size() const override {
+      return bytes_.size();
+    }
+    void Serialize(BitInserter& bit_inserter) const override {
+      for (auto&& b : bytes_) {
+        bit_inserter.insert_byte(b);
+      }
+    }
+
+   private:
+    std::vector<uint8_t> bytes_;
+  };
+
+  ::grpc::Status EnqueueCommandWithComplete(::grpc::ServerContext* context, const ::bluetooth::hci::CommandMsg* command,
+                                            ::google::protobuf::Empty* response) override {
+    auto packet = std::make_unique<TestCommandBuilder>(
+        std::vector<uint8_t>(command->command().begin(), command->command().end()));
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               facade_handler_->BindOnceOn(this, &HciLayerFacadeService::on_complete));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status EnqueueCommandWithStatus(::grpc::ServerContext* context, const ::bluetooth::hci::CommandMsg* command,
+                                          ::google::protobuf::Empty* response) override {
+    auto packet = std::make_unique<TestCommandBuilder>(
+        std::vector<uint8_t>(command->command().begin(), command->command().end()));
+    hci_layer_->EnqueueCommand(std::move(packet), facade_handler_->BindOnceOn(this, &HciLayerFacadeService::on_status));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status RegisterEventHandler(::grpc::ServerContext* context, const ::bluetooth::hci::EventCodeMsg* event,
+                                      ::google::protobuf::Empty* response) override {
+    hci_layer_->RegisterEventHandler(static_cast<EventCode>(event->code()),
+                                     facade_handler_->BindOn(this, &HciLayerFacadeService::on_event));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status RegisterLeEventHandler(::grpc::ServerContext* context,
+                                        const ::bluetooth::hci::LeSubeventCodeMsg* event,
+                                        ::google::protobuf::Empty* response) override {
+    hci_layer_->RegisterLeEventHandler(static_cast<SubeventCode>(event->code()),
+                                       facade_handler_->BindOn(this, &HciLayerFacadeService::on_le_subevent));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchEvents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                             ::grpc::ServerWriter<EventMsg>* writer) override {
+    return pending_events_.RunLoop(context, writer);
+  };
+
+  ::grpc::Status FetchLeSubevents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                  ::grpc::ServerWriter<LeSubeventMsg>* writer) override {
+    return pending_le_events_.RunLoop(context, writer);
+  };
+
+  class TestAclBuilder : public AclPacketBuilder {
+   public:
+    explicit TestAclBuilder(uint16_t handle, uint8_t packet_boundary_flag, uint8_t broadcast_flag,
+                            std::vector<uint8_t> payload)
+        : AclPacketBuilder(0xbad, PacketBoundaryFlag::CONTINUING_FRAGMENT, BroadcastFlag::ACTIVE_SLAVE_BROADCAST),
+          handle_(handle), pb_flag_(packet_boundary_flag), b_flag_(broadcast_flag), bytes_(std::move(payload)) {}
+
+    size_t size() const override {
+      return bytes_.size();
+    }
+    void Serialize(BitInserter& bit_inserter) const override {
+      LOG_INFO("handle 0x%hx boundary 0x%hhx broadcast 0x%hhx", handle_, pb_flag_, b_flag_);
+      bit_inserter.insert_byte(handle_);
+      bit_inserter.insert_bits((handle_ >> 8) & 0xf, 4);
+      bit_inserter.insert_bits(pb_flag_, 2);
+      bit_inserter.insert_bits(b_flag_, 2);
+      bit_inserter.insert_byte(bytes_.size() & 0xff);
+      bit_inserter.insert_byte((bytes_.size() & 0xff00) >> 8);
+      for (auto&& b : bytes_) {
+        bit_inserter.insert_byte(b);
+      }
+    }
+
+   private:
+    uint16_t handle_;
+    uint8_t pb_flag_;
+    uint8_t b_flag_;
+    std::vector<uint8_t> bytes_;
+  };
+
+  ::grpc::Status SendAclData(::grpc::ServerContext* context, const ::bluetooth::hci::AclMsg* acl,
+                             ::google::protobuf::Empty* response) override {
+    waiting_acl_packet_ =
+        std::make_unique<TestAclBuilder>(acl->handle(), acl->packet_boundary_flag(), acl->broadcast_flag(),
+                                         std::vector<uint8_t>(acl->data().begin(), acl->data().end()));
+    std::promise<void> enqueued;
+    auto future = enqueued.get_future();
+    if (!completed_packets_callback_registered_) {
+      controller_->RegisterCompletedAclPacketsCallback(
+          facade_handler_->Bind([](uint16_t, uint16_t) { /* do nothing */ }));
+      completed_packets_callback_registered_ = true;
+    }
+    hci_layer_->GetAclQueueEnd()->RegisterEnqueue(
+        facade_handler_, common::Bind(&HciLayerFacadeService::handle_enqueue_acl, common::Unretained(this),
+                                      common::Unretained(&enqueued)));
+    auto result = future.wait_for(std::chrono::milliseconds(100));
+    ASSERT(std::future_status::ready == result);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchAclPackets(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                 ::grpc::ServerWriter<AclMsg>* writer) override {
+    hci_layer_->GetAclQueueEnd()->RegisterDequeue(
+        facade_handler_, common::Bind(&HciLayerFacadeService::on_acl_ready, common::Unretained(this)));
+    unregister_acl_dequeue_ = true;
+    return pending_acl_events_.RunLoop(context, writer);
+  };
+
+ private:
+  std::unique_ptr<AclPacketBuilder> handle_enqueue_acl(std::promise<void>* promise) {
+    promise->set_value();
+    hci_layer_->GetAclQueueEnd()->UnregisterEnqueue();
+    return std::move(waiting_acl_packet_);
+  }
+
+  void on_acl_ready() {
+    auto acl_ptr = hci_layer_->GetAclQueueEnd()->TryDequeue();
+    ASSERT(acl_ptr != nullptr);
+    ASSERT(acl_ptr->IsValid());
+    LOG_INFO("Got an Acl message for handle 0x%hx", acl_ptr->GetHandle());
+    AclMsg incoming;
+    incoming.set_data(std::string(acl_ptr->begin(), acl_ptr->end()));
+    pending_acl_events_.OnIncomingEvent(std::move(incoming));
+  }
+
+  void on_event(hci::EventPacketView view) {
+    ASSERT(view.IsValid());
+    LOG_INFO("Got an Event %s", EventCodeText(view.GetEventCode()).c_str());
+    EventMsg response;
+    response.set_event(std::string(view.begin(), view.end()));
+    pending_events_.OnIncomingEvent(std::move(response));
+  }
+
+  void on_le_subevent(hci::LeMetaEventView view) {
+    ASSERT(view.IsValid());
+    LOG_INFO("Got an LE Event %s", SubeventCodeText(view.GetSubeventCode()).c_str());
+    LeSubeventMsg response;
+    response.set_event(std::string(view.begin(), view.end()));
+    pending_le_events_.OnIncomingEvent(std::move(response));
+  }
+
+  void on_complete(hci::CommandCompleteView view) {
+    ASSERT(view.IsValid());
+    LOG_INFO("Got a Command complete %s", OpCodeText(view.GetCommandOpCode()).c_str());
+    EventMsg response;
+    response.set_event(std::string(view.begin(), view.end()));
+    pending_events_.OnIncomingEvent(std::move(response));
+  }
+
+  void on_status(hci::CommandStatusView view) {
+    ASSERT(view.IsValid());
+    LOG_INFO("Got a Command status %s", OpCodeText(view.GetCommandOpCode()).c_str());
+    EventMsg response;
+    response.set_event(std::string(view.begin(), view.end()));
+    pending_events_.OnIncomingEvent(std::move(response));
+  }
+
+  HciLayer* hci_layer_;
+  Controller* controller_;
+  ::bluetooth::os::Handler* facade_handler_;
+  ::bluetooth::grpc::GrpcEventQueue<EventMsg> pending_events_{"FetchHciEvent"};
+  ::bluetooth::grpc::GrpcEventQueue<LeSubeventMsg> pending_le_events_{"FetchLeSubevent"};
+  ::bluetooth::grpc::GrpcEventQueue<AclMsg> pending_acl_events_{"FetchAclData"};
+  bool unregister_acl_dequeue_{false};
+  std::unique_ptr<TestAclBuilder> waiting_acl_packet_;
+  bool completed_packets_callback_registered_{false};
+};
+
+void HciLayerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<HciLayer>();
+  list->add<Controller>();
+}
+
+void HciLayerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new HciLayerFacadeService(GetDependency<HciLayer>(), GetDependency<Controller>(), GetHandler());
+}
+
+void HciLayerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* HciLayerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory HciLayerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new HciLayerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/cert/cert.h b/gd/hci/facade/facade.h
similarity index 82%
copy from gd/hci/cert/cert.h
copy to gd/hci/facade/facade.h
index e3ecf46..88ba36f 100644
--- a/gd/hci/cert/cert.h
+++ b/gd/hci/facade/facade.h
@@ -19,15 +19,15 @@
 #include <grpc++/grpc++.h>
 
 #include "grpc/grpc_module.h"
-#include "hci/acl_manager.h"
+#include "hci/hci_layer.h"
 
 namespace bluetooth {
 namespace hci {
-namespace cert {
+namespace facade {
 
-class AclManagerCertService;
+class HciLayerFacadeService;
 
-class AclManagerCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class HciLayerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
@@ -37,9 +37,9 @@
   ::grpc::Service* GetService() const override;
 
  private:
-  AclManagerCertService* service_;
+  HciLayerFacadeService* service_;
 };
 
-}  // namespace cert
+}  // namespace facade
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/facade/facade.proto b/gd/hci/facade/facade.proto
new file mode 100644
index 0000000..bcce542
--- /dev/null
+++ b/gd/hci/facade/facade.proto
@@ -0,0 +1,43 @@
+syntax = "proto3";
+
+package bluetooth.hci;
+
+import "google/protobuf/empty.proto";
+
+service HciLayerFacade {
+  rpc EnqueueCommandWithComplete(CommandMsg) returns (google.protobuf.Empty) {}
+  rpc EnqueueCommandWithStatus(CommandMsg) returns (google.protobuf.Empty) {}
+  rpc RegisterEventHandler(EventCodeMsg) returns (google.protobuf.Empty) {}
+  rpc RegisterLeEventHandler(LeSubeventCodeMsg) returns (google.protobuf.Empty) {}
+  rpc SendAclData(AclMsg) returns (google.protobuf.Empty) {}
+  rpc FetchEvents(google.protobuf.Empty) returns (stream EventMsg) {}
+  rpc FetchLeSubevents(google.protobuf.Empty) returns (stream LeSubeventMsg) {}
+  rpc FetchAclPackets(google.protobuf.Empty) returns (stream AclMsg) {}
+}
+
+message CommandMsg {
+  bytes command = 1;
+}
+
+message EventMsg {
+  bytes event = 1;
+}
+
+message LeSubeventMsg {
+  bytes event = 1;
+}
+
+message AclMsg {
+  uint32 handle = 1;
+  uint32 packet_boundary_flag = 2;
+  uint32 broadcast_flag = 3;
+  bytes data = 4;
+}
+
+message EventCodeMsg {
+  uint32 code = 1;
+}
+
+message LeSubeventCodeMsg {
+  uint32 code = 1;
+}
diff --git a/gd/hci/facade/le_acl_manager_facade.cc b/gd/hci/facade/le_acl_manager_facade.cc
new file mode 100644
index 0000000..92e9c56
--- /dev/null
+++ b/gd/hci/facade/le_acl_manager_facade.cc
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/facade/le_acl_manager_facade.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/acl_manager.h"
+#include "hci/facade/le_acl_manager_facade.grpc.pb.h"
+#include "hci/facade/le_acl_manager_facade.pb.h"
+#include "hci/hci_packets.h"
+#include "packet/raw_builder.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+using ::bluetooth::packet::RawBuilder;
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+using acl_manager::LeAclConnection;
+using acl_manager::LeConnectionCallbacks;
+using acl_manager::LeConnectionManagementCallbacks;
+
+class LeAclManagerFacadeService : public LeAclManagerFacade::Service, public LeConnectionCallbacks {
+ public:
+  LeAclManagerFacadeService(AclManager* acl_manager, ::bluetooth::os::Handler* facade_handler)
+      : acl_manager_(acl_manager), facade_handler_(facade_handler) {
+    acl_manager_->RegisterLeCallbacks(this, facade_handler_);
+  }
+
+  ~LeAclManagerFacadeService() override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    for (auto& conn : acl_connections_) {
+      if (conn.second.connection_ != nullptr) {
+        conn.second.connection_->GetAclQueueEnd()->UnregisterDequeue();
+        conn.second.connection_.reset();
+      }
+    }
+  }
+
+  ::grpc::Status CreateConnection(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::facade::BluetoothAddressWithType* request,
+      ::grpc::ServerWriter<LeConnectionEvent>* writer) override {
+    Address peer_address;
+    ASSERT(Address::FromString(request->address().address(), peer_address));
+    AddressWithType peer(peer_address, static_cast<AddressType>(request->type()));
+    acl_manager_->CreateLeConnection(peer);
+    if (per_connection_events_.size() > current_connection_request_) {
+      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding request is supported");
+    }
+    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
+        std::string("connection attempt ") + std::to_string(current_connection_request_)));
+    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+  }
+
+  ::grpc::Status CancelConnection(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::facade::BluetoothAddressWithType* request,
+      google::protobuf::Empty* response) override {
+    Address peer_address;
+    ASSERT(Address::FromString(request->address().address(), peer_address));
+    AddressWithType peer(peer_address, static_cast<AddressType>(request->type()));
+    if (per_connection_events_.size() == current_connection_request_) {
+      // Todo: Check that the address matches an outstanding connection request
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "No matching outstanding connection");
+    }
+    acl_manager_->CancelLeConnect(peer);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status Disconnect(::grpc::ServerContext* context, const LeHandleMsg* request,
+                            ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto connection = acl_connections_.find(request->handle());
+    if (connection == acl_connections_.end()) {
+      LOG_ERROR("Invalid handle");
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+    } else {
+      connection->second.connection_->Disconnect(DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
+      return ::grpc::Status::OK;
+    }
+  }
+
+#define GET_CONNECTION(view)                                                         \
+  std::map<uint16_t, Connection>::iterator connection;                               \
+  do {                                                                               \
+    if (!view.IsValid()) {                                                           \
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \
+    }                                                                                \
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);                       \
+    connection = acl_connections_.find(view.GetConnectionHandle());                  \
+    if (connection == acl_connections_.end()) {                                      \
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \
+    }                                                                                \
+  } while (0)
+
+  ::grpc::Status ConnectionCommand(
+      ::grpc::ServerContext* context,
+      const LeConnectionCommandMsg* request,
+      ::google::protobuf::Empty* response) override {
+    auto command_view = ConnectionManagementCommandView::Create(CommandPacketView::Create(
+        std::make_shared<std::vector<uint8_t>>(request->packet().begin(), request->packet().end())));
+    if (!command_view.IsValid()) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet");
+    }
+    switch (command_view.GetOpCode()) {
+      case OpCode::DISCONNECT: {
+        auto view = DisconnectView::Create(command_view);
+        GET_CONNECTION(view);
+        connection->second.connection_->Disconnect(view.GetReason());
+        return ::grpc::Status::OK;
+      }
+      default:
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet");
+    }
+  }
+#undef GET_CONNECTION
+
+  ::grpc::Status FetchIncomingConnection(
+      ::grpc::ServerContext* context,
+      const google::protobuf::Empty* request,
+      ::grpc::ServerWriter<LeConnectionEvent>* writer) override {
+    if (per_connection_events_.size() > current_connection_request_) {
+      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding connection is supported");
+    }
+    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
+        std::string("incoming connection ") + std::to_string(current_connection_request_)));
+    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+  }
+
+  ::grpc::Status SendAclData(
+      ::grpc::ServerContext* context, const LeAclData* request, ::google::protobuf::Empty* response) override {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    {
+      std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+      auto connection = acl_connections_.find(request->handle());
+      if (connection == acl_connections_.end()) {
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+      }
+      connection->second.connection_->GetAclQueueEnd()->RegisterEnqueue(
+          facade_handler_,
+          common::Bind(
+              &LeAclManagerFacadeService::enqueue_packet,
+              common::Unretained(this),
+              common::Unretained(request),
+              common::Passed(std::move(promise))));
+      auto status = future.wait_for(std::chrono::milliseconds(1000));
+      if (status != std::future_status::ready) {
+        return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Can't send packet");
+      }
+    }
+    return ::grpc::Status::OK;
+  }
+
+  std::unique_ptr<BasePacketBuilder> enqueue_packet(const LeAclData* request, std::promise<void> promise) {
+    auto connection = acl_connections_.find(request->handle());
+    ASSERT_LOG(connection != acl_connections_.end(), "handle %d", request->handle());
+    connection->second.connection_->GetAclQueueEnd()->UnregisterEnqueue();
+    std::unique_ptr<RawBuilder> packet =
+        std::make_unique<RawBuilder>(std::vector<uint8_t>(request->payload().begin(), request->payload().end()));
+    promise.set_value();
+    return packet;
+  }
+
+  ::grpc::Status FetchAclData(
+      ::grpc::ServerContext* context, const LeHandleMsg* request, ::grpc::ServerWriter<LeAclData>* writer) override {
+    auto connection = acl_connections_.find(request->handle());
+    if (connection == acl_connections_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+    }
+    return connection->second.pending_acl_data_.RunLoop(context, writer);
+  }
+
+  static inline uint16_t to_handle(uint32_t current_request) {
+    return (current_request + 0x10) % 0xe00;
+  }
+
+  static inline std::string builder_to_string(std::unique_ptr<BasePacketBuilder> builder) {
+    std::vector<uint8_t> bytes;
+    BitInserter bit_inserter(bytes);
+    builder->Serialize(bit_inserter);
+    return std::string(bytes.begin(), bytes.end());
+  }
+
+  void on_incoming_acl(std::shared_ptr<LeAclConnection> connection, uint16_t handle) {
+    auto packet = connection->GetAclQueueEnd()->TryDequeue();
+    auto connection_tracker = acl_connections_.find(handle);
+    ASSERT_LOG(connection_tracker != acl_connections_.end(), "handle %d", handle);
+    LeAclData acl_data;
+    acl_data.set_handle(handle);
+    acl_data.set_payload(std::string(packet->begin(), packet->end()));
+    connection_tracker->second.pending_acl_data_.OnIncomingEvent(acl_data);
+  }
+
+  void OnLeConnectSuccess(AddressWithType address_with_type, std::unique_ptr<LeAclConnection> connection) override {
+    LOG_DEBUG("%s", address_with_type.ToString().c_str());
+
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto addr = address_with_type.GetAddress();
+    std::shared_ptr<LeAclConnection> shared_connection = std::move(connection);
+    uint16_t handle = to_handle(current_connection_request_);
+    acl_connections_.emplace(
+        std::piecewise_construct,
+        std::forward_as_tuple(handle),
+        std::forward_as_tuple(handle, shared_connection, per_connection_events_[current_connection_request_]));
+    shared_connection->GetAclQueueEnd()->RegisterDequeue(
+        facade_handler_,
+        common::Bind(&LeAclManagerFacadeService::on_incoming_acl, common::Unretained(this), shared_connection, handle));
+    auto callbacks = acl_connections_.find(handle)->second.GetCallbacks();
+    shared_connection->RegisterCallbacks(callbacks, facade_handler_);
+    {
+      std::unique_ptr<BasePacketBuilder> builder = LeConnectionCompleteBuilder::Create(
+          ErrorCode::SUCCESS,
+          handle,
+          Role::MASTER,
+          address_with_type.GetAddressType(),
+          addr,
+          1,
+          2,
+          3,
+          ClockAccuracy::PPM_20);
+      LeConnectionEvent success;
+      success.set_event(builder_to_string(std::move(builder)));
+      per_connection_events_[current_connection_request_]->OnIncomingEvent(success);
+    }
+    current_connection_request_++;
+  }
+
+  void OnLeConnectFail(AddressWithType address, ErrorCode reason) override {
+    std::unique_ptr<BasePacketBuilder> builder = LeConnectionCompleteBuilder::Create(
+        reason, 0, Role::MASTER, address.GetAddressType(), address.GetAddress(), 0, 0, 0, ClockAccuracy::PPM_20);
+    LeConnectionEvent fail;
+    fail.set_event(builder_to_string(std::move(builder)));
+    per_connection_events_[current_connection_request_]->OnIncomingEvent(fail);
+    current_connection_request_++;
+  }
+
+  class Connection : public LeConnectionManagementCallbacks {
+   public:
+    Connection(
+        uint16_t handle,
+        std::shared_ptr<LeAclConnection> connection,
+        std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> event_stream)
+        : handle_(handle), connection_(std::move(connection)), event_stream_(std::move(event_stream)) {}
+    void OnConnectionUpdate(
+        uint16_t connection_interval, uint16_t connection_latency, uint16_t supervision_timeout) override {
+      LOG_DEBUG(
+          "interval: 0x%hx, latency: 0x%hx, timeout 0x%hx",
+          connection_interval,
+          connection_latency,
+          supervision_timeout);
+    }
+
+    void OnDisconnection(ErrorCode reason) override {
+      std::unique_ptr<BasePacketBuilder> builder =
+          DisconnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, reason);
+      LeConnectionEvent disconnection;
+      disconnection.set_event(builder_to_string(std::move(builder)));
+      event_stream_->OnIncomingEvent(disconnection);
+    }
+
+    LeConnectionManagementCallbacks* GetCallbacks() {
+      return this;
+    }
+
+    uint16_t handle_;
+    std::shared_ptr<LeAclConnection> connection_;
+    std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> event_stream_;
+    ::bluetooth::grpc::GrpcEventQueue<LeAclData> pending_acl_data_{std::string("PendingAclData") +
+                                                                   std::to_string(handle_)};
+  };
+
+ private:
+  AclManager* acl_manager_;
+  ::bluetooth::os::Handler* facade_handler_;
+  mutable std::mutex acl_connections_mutex_;
+  std::vector<std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>> per_connection_events_;
+  std::map<uint16_t, Connection> acl_connections_;
+  uint32_t current_connection_request_{0};
+};
+
+void LeAclManagerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<AclManager>();
+}
+
+void LeAclManagerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new LeAclManagerFacadeService(GetDependency<AclManager>(), GetHandler());
+}
+
+void LeAclManagerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* LeAclManagerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory LeAclManagerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new LeAclManagerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/cert/cert.h b/gd/hci/facade/le_acl_manager_facade.h
similarity index 83%
rename from gd/hci/cert/cert.h
rename to gd/hci/facade/le_acl_manager_facade.h
index e3ecf46..899a6cc 100644
--- a/gd/hci/cert/cert.h
+++ b/gd/hci/facade/le_acl_manager_facade.h
@@ -23,11 +23,11 @@
 
 namespace bluetooth {
 namespace hci {
-namespace cert {
+namespace facade {
 
-class AclManagerCertService;
+class LeAclManagerFacadeService;
 
-class AclManagerCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class LeAclManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
@@ -37,9 +37,9 @@
   ::grpc::Service* GetService() const override;
 
  private:
-  AclManagerCertService* service_;
+  LeAclManagerFacadeService* service_;
 };
 
-}  // namespace cert
+}  // namespace facade
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/facade/le_acl_manager_facade.proto b/gd/hci/facade/le_acl_manager_facade.proto
new file mode 100644
index 0000000..219a423
--- /dev/null
+++ b/gd/hci/facade/le_acl_manager_facade.proto
@@ -0,0 +1,34 @@
+syntax = "proto3";
+
+package bluetooth.hci;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service LeAclManagerFacade {
+  rpc CreateConnection(bluetooth.facade.BluetoothAddressWithType) returns (stream LeConnectionEvent) {}
+  rpc CancelConnection(bluetooth.facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {}
+  rpc Disconnect(LeHandleMsg) returns (google.protobuf.Empty) {}
+  rpc ConnectionCommand(LeConnectionCommandMsg) returns (google.protobuf.Empty) {}
+  rpc SendAclData(LeAclData) returns (google.protobuf.Empty) {}
+  rpc FetchAclData(LeHandleMsg) returns (stream LeAclData) {}
+  rpc FetchIncomingConnection(google.protobuf.Empty) returns (stream LeConnectionEvent) {}
+}
+
+message LeHandleMsg {
+  uint32 handle = 1;
+}
+
+message LeConnectionEvent {
+  bytes event = 1;
+}
+
+message LeConnectionCommandMsg {
+  bytes packet = 1;
+}
+
+message LeAclData {
+  uint32 handle = 1;
+  bytes payload = 2;
+}
+
diff --git a/gd/hci/facade/le_advertising_manager_facade.cc b/gd/hci/facade/le_advertising_manager_facade.cc
index c01beae..12afdd2 100644
--- a/gd/hci/facade/le_advertising_manager_facade.cc
+++ b/gd/hci/facade/le_advertising_manager_facade.cc
@@ -27,7 +27,6 @@
 #include "hci/facade/le_advertising_manager_facade.pb.h"
 #include "hci/le_advertising_manager.h"
 #include "os/log.h"
-#include "packet/raw_builder.h"
 
 namespace bluetooth {
 namespace hci {
@@ -42,7 +41,7 @@
 using ::bluetooth::facade::BluetoothAddress;
 using ::bluetooth::facade::BluetoothAddressTypeEnum;
 
-hci::GapData GapDataFromProto(const GapData& gap_data_proto) {
+hci::GapData GapDataFromProto(const GapDataMsg& gap_data_proto) {
   hci::GapData gap_data;
   auto data_copy = std::make_shared<std::vector<uint8_t>>(gap_data_proto.data().begin(), gap_data_proto.data().end());
   packet::PacketView<packet::kLittleEndian> packet(data_copy);
@@ -60,8 +59,6 @@
     config->scan_response.push_back(GapDataFromProto(elem));
   }
 
-  hci::Address::FromString(config_proto.random_address().address(), config->random_address);
-
   if (config_proto.interval_min() > UINT16_MAX || config_proto.interval_min() < 0) {
     LOG_WARN("Bad interval_min: %d", config_proto.interval_min());
     return false;
@@ -74,7 +71,7 @@
   }
   config->interval_max = static_cast<uint16_t>(config_proto.interval_max());
 
-  config->event_type = static_cast<hci::AdvertisingEventType>(config_proto.event_type());
+  config->event_type = static_cast<hci::AdvertisingType>(config_proto.event_type());
 
   config->address_type = static_cast<::bluetooth::hci::AddressType>(config_proto.address_type());
 
@@ -128,9 +125,8 @@
     ASSERT(facade_handler_ != nullptr);
   }
 
-  ::grpc::Status CreateAdvertiser(::grpc::ServerContext* context,
-                                  const ::bluetooth::hci::facade::CreateAdvertiserRequest* request,
-                                  ::bluetooth::hci::facade::CreateAdvertiserResponse* response) override {
+  ::grpc::Status CreateAdvertiser(::grpc::ServerContext* context, const CreateAdvertiserRequest* request,
+                                  CreateAdvertiserResponse* response) override {
     hci::AdvertisingConfig config = {};
     if (!AdvertisingConfigFromProto(request->config(), &config)) {
       LOG_WARN("Error parsing advertising config %s", request->SerializeAsString().c_str());
@@ -151,23 +147,22 @@
     return ::grpc::Status::OK;
   }
 
-  ::grpc::Status ExtendedCreateAdvertiser(
-      ::grpc::ServerContext* context, const ::bluetooth::hci::facade::ExtendedCreateAdvertiserRequest* request,
-      ::bluetooth::hci::facade::ExtendedCreateAdvertiserResponse* response) override {
+  ::grpc::Status ExtendedCreateAdvertiser(::grpc::ServerContext* context,
+                                          const ExtendedCreateAdvertiserRequest* request,
+                                          ExtendedCreateAdvertiserResponse* response) override {
     LOG_WARN("ExtendedCreateAdvertiser is not implemented");
     response->set_advertiser_id(LeAdvertisingManager::kInvalidId);
     return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "ExtendedCreateAdvertiser is not implemented");
   }
 
-  ::grpc::Status GetNumberOfAdvertisingInstances(
-      ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-      ::bluetooth::hci::facade::GetNumberOfAdvertisingInstancesResponse* response) override {
+  ::grpc::Status GetNumberOfAdvertisingInstances(::grpc::ServerContext* context,
+                                                 const ::google::protobuf::Empty* request,
+                                                 GetNumberOfAdvertisingInstancesResponse* response) override {
     response->set_num_advertising_instances(le_advertising_manager_->GetNumberOfAdvertisingInstances());
     return ::grpc::Status::OK;
   }
 
-  ::grpc::Status RemoveAdvertiser(::grpc::ServerContext* context,
-                                  const ::bluetooth::hci::facade::RemoveAdvertiserRequest* request,
+  ::grpc::Status RemoveAdvertiser(::grpc::ServerContext* context, const RemoveAdvertiserRequest* request,
                                   ::google::protobuf::Empty* response) override {
     if (request->advertiser_id() == LeAdvertisingManager::kInvalidId) {
       LOG_WARN("Invalid advertiser ID %d", request->advertiser_id());
diff --git a/gd/hci/facade/le_advertising_manager_facade.proto b/gd/hci/facade/le_advertising_manager_facade.proto
index f3fdca5..25e5cbf 100644
--- a/gd/hci/facade/le_advertising_manager_facade.proto
+++ b/gd/hci/facade/le_advertising_manager_facade.proto
@@ -12,7 +12,7 @@
   rpc RemoveAdvertiser(RemoveAdvertiserRequest) returns (google.protobuf.Empty) {}
 }
 
-message GapData {
+message GapDataMsg {
   bytes data = 1;
 }
 
@@ -26,15 +26,14 @@
 
 enum AdvertisingFilterPolicy {
   ALL_DEVICES = 0x0;
-  WHITELISTED_SCAN = 0x1;
-  WHITELISTED_CONNECT = 0x2;
-  WHITELISTED_SCAN_AND_CONNECT = 0x3;
+  LISTED_SCAN = 0x1;
+  LISTED_CONNECT = 0x2;
+  LISTED_SCAN_AND_CONNECT = 0x3;
 };
 
 message AdvertisingConfig {
-  repeated GapData advertisement = 1;
-  repeated GapData scan_response = 2;
-  bluetooth.facade.BluetoothAddress random_address = 3;
+  repeated GapDataMsg advertisement = 1;
+  repeated GapDataMsg scan_response = 2;
   // Unit: number of Bluetooth slots in 0.125 ms increment
   int32 interval_min = 4;
   // Unit: number of Bluetooth slots in 0.125 ms increment
@@ -87,4 +86,4 @@
 
 message RemoveAdvertiserRequest {
   int32 advertiser_id = 1;
-}
\ No newline at end of file
+}
diff --git a/gd/hci/facade/le_initiator_address_facade.cc b/gd/hci/facade/le_initiator_address_facade.cc
new file mode 100644
index 0000000..4b82e42
--- /dev/null
+++ b/gd/hci/facade/le_initiator_address_facade.cc
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+#include "hci/facade/le_initiator_address_facade.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/acl_manager.h"
+#include "hci/facade/le_initiator_address_facade.grpc.pb.h"
+#include "hci/facade/le_initiator_address_facade.pb.h"
+#include "hci/hci_packets.h"
+#include "packet/raw_builder.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+using ::bluetooth::packet::RawBuilder;
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class LeInitiatorAddressFacadeService : public LeInitiatorAddressFacade::Service {
+ public:
+  LeInitiatorAddressFacadeService(AclManager* acl_manager, ::bluetooth::os::Handler* facade_handler)
+      : acl_manager_(acl_manager),
+        address_manager_(acl_manager_->GetLeAddressManager()),
+        facade_handler_(facade_handler) {
+    ASSERT(facade_handler_ != nullptr);
+  }
+
+  ::grpc::Status SetPrivacyPolicyForInitiatorAddress(
+      ::grpc::ServerContext* context, const PrivacyPolicy* request, ::google::protobuf::Empty* writer) override {
+    Address address = Address::kEmpty;
+    LeAddressManager::AddressPolicy address_policy =
+        static_cast<LeAddressManager::AddressPolicy>(request->address_policy());
+    if (address_policy == LeAddressManager::AddressPolicy::USE_STATIC_ADDRESS) {
+      ASSERT(Address::FromString(request->address_with_type().address().address(), address));
+    }
+    AddressWithType address_with_type(address, static_cast<AddressType>(request->address_with_type().type()));
+    crypto_toolbox::Octet16 irk = {};
+    auto request_irk_length = request->rotation_irk().end() - request->rotation_irk().begin();
+    if (request_irk_length == crypto_toolbox::OCTET16_LEN) {
+      std::vector<uint8_t> irk_data(request->rotation_irk().begin(), request->rotation_irk().end());
+      std::copy_n(irk_data.begin(), crypto_toolbox::OCTET16_LEN, irk.begin());
+    } else {
+      ASSERT(request_irk_length == 0);
+    }
+    auto minimum_rotation_time = std::chrono::milliseconds(request->minimum_rotation_time());
+    auto maximum_rotation_time = std::chrono::milliseconds(request->maximum_rotation_time());
+    acl_manager_->SetPrivacyPolicyForInitiatorAddress(
+        address_policy, address_with_type, irk, minimum_rotation_time, maximum_rotation_time);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status GetCurrentInitiatorAddress(
+      ::grpc::ServerContext* context,
+      const ::google::protobuf::Empty* request,
+      ::bluetooth::facade::BluetoothAddressWithType* response) override {
+    AddressWithType current = address_manager_->GetCurrentAddress();
+    auto bluetooth_address = new ::bluetooth::facade::BluetoothAddress();
+    bluetooth_address->set_address(current.GetAddress().ToString());
+    response->set_type(static_cast<::bluetooth::facade::BluetoothAddressTypeEnum>(current.GetAddressType()));
+    response->set_allocated_address(bluetooth_address);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status GetAnotherAddress(
+      ::grpc::ServerContext* context,
+      const ::google::protobuf::Empty* request,
+      ::bluetooth::facade::BluetoothAddressWithType* response) override {
+    AddressWithType another = address_manager_->GetAnotherAddress();
+    auto bluetooth_address = new ::bluetooth::facade::BluetoothAddress();
+    bluetooth_address->set_address(another.GetAddress().ToString());
+    response->set_type(static_cast<::bluetooth::facade::BluetoothAddressTypeEnum>(another.GetAddressType()));
+    response->set_allocated_address(bluetooth_address);
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  AclManager* acl_manager_;
+  LeAddressManager* address_manager_;
+  ::bluetooth::os::Handler* facade_handler_;
+};
+
+void LeInitiatorAddressFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<AclManager>();
+}
+
+void LeInitiatorAddressFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new LeInitiatorAddressFacadeService(GetDependency<AclManager>(), GetHandler());
+}
+
+void LeInitiatorAddressFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* LeInitiatorAddressFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory LeInitiatorAddressFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new LeInitiatorAddressFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/cert/cert.h b/gd/hci/facade/le_initiator_address_facade.h
similarity index 78%
copy from gd/hci/cert/cert.h
copy to gd/hci/facade/le_initiator_address_facade.h
index e3ecf46..9e1a42d 100644
--- a/gd/hci/cert/cert.h
+++ b/gd/hci/facade/le_initiator_address_facade.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -23,11 +23,11 @@
 
 namespace bluetooth {
 namespace hci {
-namespace cert {
+namespace facade {
 
-class AclManagerCertService;
+class LeInitiatorAddressFacadeService;
 
-class AclManagerCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class LeInitiatorAddressFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
@@ -37,9 +37,9 @@
   ::grpc::Service* GetService() const override;
 
  private:
-  AclManagerCertService* service_;
+  LeInitiatorAddressFacadeService* service_;
 };
 
-}  // namespace cert
+}  // namespace facade
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/facade/le_initiator_address_facade.proto b/gd/hci/facade/le_initiator_address_facade.proto
new file mode 100644
index 0000000..50aeea2
--- /dev/null
+++ b/gd/hci/facade/le_initiator_address_facade.proto
@@ -0,0 +1,28 @@
+syntax = "proto3";
+
+package bluetooth.hci;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service LeInitiatorAddressFacade {
+  rpc SetPrivacyPolicyForInitiatorAddress(PrivacyPolicy) returns (google.protobuf.Empty) {}
+  rpc GetCurrentInitiatorAddress(google.protobuf.Empty) returns (bluetooth.facade.BluetoothAddressWithType) {}
+  rpc GetAnotherAddress(google.protobuf.Empty) returns (bluetooth.facade.BluetoothAddressWithType) {}
+}
+
+enum AddressPolicy {
+  POLICY_NOT_SET = 0x00;
+  USE_PUBLIC_ADDRESS = 0x01;
+  USE_STATIC_ADDRESS = 0x02;
+  USE_NON_RESOLVABLE_ADDRESS = 0x03;
+  USE_RESOLVABLE_ADDRESS = 0x04;
+}
+
+message PrivacyPolicy {
+  AddressPolicy address_policy = 1;
+  facade.BluetoothAddressWithType address_with_type = 2;
+  bytes rotation_irk = 3;
+  uint64 minimum_rotation_time = 4;
+  uint64 maximum_rotation_time = 5;
+}
diff --git a/gd/hci/facade/le_scanning_manager_facade.cc b/gd/hci/facade/le_scanning_manager_facade.cc
new file mode 100644
index 0000000..bf790b7
--- /dev/null
+++ b/gd/hci/facade/le_scanning_manager_facade.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/le_scanning_manager.h"
+
+#include <cstdint>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bidi_queue.h"
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/facade/le_scanning_manager_facade.grpc.pb.h"
+#include "hci/facade/le_scanning_manager_facade.h"
+#include "hci/facade/le_scanning_manager_facade.pb.h"
+#include "hci/le_report.h"
+#include "os/log.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+using ::grpc::ServerWriter;
+using ::grpc::Status;
+
+class LeScanningManagerFacadeService : public LeScanningManagerFacade::Service, public LeScanningManagerCallbacks {
+ public:
+  LeScanningManagerFacadeService(LeScanningManager* le_scanning_manager, os::Handler* facade_handler)
+      : le_scanning_manager_(le_scanning_manager), facade_handler_(facade_handler) {
+    ASSERT(le_scanning_manager_ != nullptr);
+    ASSERT(facade_handler_ != nullptr);
+  }
+
+  ::grpc::Status StartScan(::grpc::ServerContext* context, const ::google::protobuf::Empty*,
+                           ::grpc::ServerWriter<LeReportMsg>* writer) override {
+    le_scanning_manager_->StartScan(this);
+    return pending_events_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status StopScan(::grpc::ServerContext* context, const ::google::protobuf::Empty*,
+                          ScanStoppedMsg* response) override {
+    std::shared_ptr<std::promise<void>> on_stopped = std::make_shared<std::promise<void>>();
+    auto future = on_stopped->get_future();
+    le_scanning_manager_->StopScan(
+        common::Bind([](std::shared_ptr<std::promise<void>> p) { p->set_value(); }, on_stopped));
+    auto result = future.wait_for(std::chrono::milliseconds(1000));
+    ASSERT(result == std::future_status::ready);
+    return ::grpc::Status::OK;
+  }
+
+  void on_advertisements(std::vector<std::shared_ptr<LeReport>> reports) override {
+    for (const auto report : reports) {
+      switch (report->report_type_) {
+        case hci::LeReport::ReportType::ADVERTISING_EVENT: {
+          LeReportMsg le_report_msg;
+          std::vector<LeAdvertisingReport> advertisements;
+          LeAdvertisingReport le_advertising_report;
+          le_advertising_report.address_type_ = report->address_type_;
+          le_advertising_report.address_ = report->address_;
+          le_advertising_report.advertising_data_ = report->gap_data_;
+          le_advertising_report.event_type_ = report->advertising_event_type_;
+          le_advertising_report.rssi_ = report->rssi_;
+          advertisements.push_back(le_advertising_report);
+
+          auto builder = LeAdvertisingReportBuilder::Create(advertisements);
+          std::vector<uint8_t> bytes;
+          BitInserter bit_inserter(bytes);
+          builder->Serialize(bit_inserter);
+          le_report_msg.set_event(std::string(bytes.begin(), bytes.end()));
+          pending_events_.OnIncomingEvent(std::move(le_report_msg));
+        } break;
+        case hci::LeReport::ReportType::EXTENDED_ADVERTISING_EVENT: {
+          LeReportMsg le_report_msg;
+          std::vector<LeExtendedAdvertisingReport> advertisements;
+          LeExtendedAdvertisingReport le_extended_advertising_report;
+          le_extended_advertising_report.address_ = report->address_;
+          le_extended_advertising_report.advertising_data_ = report->gap_data_;
+          le_extended_advertising_report.rssi_ = report->rssi_;
+          advertisements.push_back(le_extended_advertising_report);
+
+          auto builder = LeExtendedAdvertisingReportBuilder::Create(advertisements);
+          std::vector<uint8_t> bytes;
+          BitInserter bit_inserter(bytes);
+          builder->Serialize(bit_inserter);
+          le_report_msg.set_event(std::string(bytes.begin(), bytes.end()));
+          pending_events_.OnIncomingEvent(std::move(le_report_msg));
+        } break;
+        case hci::LeReport::ReportType::DIRECTED_ADVERTISING_EVENT: {
+          LeReportMsg le_report_msg;
+          std::vector<LeDirectedAdvertisingReport> advertisements;
+          LeDirectedAdvertisingReport le_directed_advertising_report;
+          le_directed_advertising_report.address_ = report->address_;
+          le_directed_advertising_report.direct_address_ = ((DirectedLeReport*)report.get())->direct_address_;
+          le_directed_advertising_report.direct_address_type_ = DirectAddressType::RANDOM_DEVICE_ADDRESS;
+          le_directed_advertising_report.event_type_ = DirectAdvertisingEventType::ADV_DIRECT_IND;
+          le_directed_advertising_report.rssi_ = report->rssi_;
+          advertisements.push_back(le_directed_advertising_report);
+
+          auto builder = LeDirectedAdvertisingReportBuilder::Create(advertisements);
+          std::vector<uint8_t> bytes;
+          BitInserter bit_inserter(bytes);
+          builder->Serialize(bit_inserter);
+          le_report_msg.set_event(std::string(bytes.begin(), bytes.end()));
+          pending_events_.OnIncomingEvent(std::move(le_report_msg));
+        } break;
+        default:
+          LOG_INFO("Skipping unknown report type %d", static_cast<int>(report->report_type_));
+      }
+    }
+  }
+
+  void on_timeout() override {
+    LeReportMsg le_report_msg;
+    auto builder = LeScanTimeoutBuilder::Create();
+    std::vector<uint8_t> bytes;
+    BitInserter bit_inserter(bytes);
+    builder->Serialize(bit_inserter);
+    le_report_msg.set_event(std::string(bytes.begin(), bytes.end()));
+    pending_events_.OnIncomingEvent(std::move(le_report_msg));
+  }
+
+  os::Handler* Handler() override {
+    return facade_handler_;
+  }
+
+  LeScanningManager* le_scanning_manager_;
+  os::Handler* facade_handler_;
+  ::bluetooth::grpc::GrpcEventQueue<LeReportMsg> pending_events_{"LeReports"};
+};
+
+void LeScanningManagerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<hci::LeScanningManager>();
+}
+
+void LeScanningManagerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new LeScanningManagerFacadeService(GetDependency<hci::LeScanningManager>(), GetHandler());
+}
+
+void LeScanningManagerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* LeScanningManagerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory LeScanningManagerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new LeScanningManagerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/cert/cert.h b/gd/hci/facade/le_scanning_manager_facade.h
similarity index 82%
copy from gd/hci/cert/cert.h
copy to gd/hci/facade/le_scanning_manager_facade.h
index e3ecf46..8731bd0 100644
--- a/gd/hci/cert/cert.h
+++ b/gd/hci/facade/le_scanning_manager_facade.h
@@ -13,33 +13,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #pragma once
 
 #include <grpc++/grpc++.h>
 
 #include "grpc/grpc_module.h"
-#include "hci/acl_manager.h"
 
 namespace bluetooth {
 namespace hci {
-namespace cert {
+namespace facade {
 
-class AclManagerCertService;
+class LeScanningManagerFacadeService;
 
-class AclManagerCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class LeScanningManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
   void ListDependencies(ModuleList* list) override;
   void Start() override;
   void Stop() override;
+
   ::grpc::Service* GetService() const override;
 
  private:
-  AclManagerCertService* service_;
+  LeScanningManagerFacadeService* service_;
 };
 
-}  // namespace cert
+}  // namespace facade
 }  // namespace hci
-}  // namespace bluetooth
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/hci/facade/le_scanning_manager_facade.proto b/gd/hci/facade/le_scanning_manager_facade.proto
new file mode 100644
index 0000000..9a450e5
--- /dev/null
+++ b/gd/hci/facade/le_scanning_manager_facade.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+
+package bluetooth.hci.facade;
+
+import "google/protobuf/empty.proto";
+
+service LeScanningManagerFacade {
+  rpc StartScan(google.protobuf.Empty) returns (stream LeReportMsg) {}
+  rpc StopScan(google.protobuf.Empty) returns (ScanStoppedMsg) {}
+}
+
+message LeReportMsg {
+  bytes event = 1;
+}
+
+message ScanStoppedMsg {}
diff --git a/gd/hci/fuzz/acl_manager_fuzz_test.cc b/gd/hci/fuzz/acl_manager_fuzz_test.cc
new file mode 100644
index 0000000..106b3bb
--- /dev/null
+++ b/gd/hci/fuzz/acl_manager_fuzz_test.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include "fuzz/helpers.h"
+#include "hci/acl_manager.h"
+#include "hci/fuzz/fuzz_hci_layer.h"
+#include "hci/hci_layer.h"
+#include "module.h"
+#include "os/fuzz/fake_timerfd.h"
+#include "os/log.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using bluetooth::FuzzTestModuleRegistry;
+using bluetooth::fuzz::GetArbitraryBytes;
+using bluetooth::hci::AclManager;
+using bluetooth::hci::HciLayer;
+using bluetooth::hci::fuzz::FuzzHciLayer;
+using bluetooth::os::fuzz::fake_timerfd_advance;
+using bluetooth::os::fuzz::fake_timerfd_cap_at;
+using bluetooth::os::fuzz::fake_timerfd_reset;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider dataProvider(data, size);
+
+  static FuzzTestModuleRegistry moduleRegistry = FuzzTestModuleRegistry();
+  FuzzHciLayer* fuzzHci = moduleRegistry.Inject<FuzzHciLayer>(&HciLayer::Factory);
+  fuzzHci->TurnOnAutoReply(&dataProvider);
+  moduleRegistry.Start<AclManager>();
+  fuzzHci->TurnOffAutoReply();
+
+  while (dataProvider.remaining_bytes() > 0) {
+    const uint8_t action = dataProvider.ConsumeIntegralInRange(0, 2);
+    switch (action) {
+      case 1:
+        fake_timerfd_advance(dataProvider.ConsumeIntegral<uint64_t>());
+        break;
+      case 2:
+        fuzzHci->injectArbitrary(dataProvider);
+        break;
+    }
+  }
+
+  moduleRegistry.WaitForIdleAndStopAll();
+  fake_timerfd_reset();
+  return 0;
+}
diff --git a/gd/hci/fuzz/fuzz_hci_layer.cc b/gd/hci/fuzz/fuzz_hci_layer.cc
new file mode 100644
index 0000000..299218c
--- /dev/null
+++ b/gd/hci/fuzz/fuzz_hci_layer.cc
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+#include "hci/fuzz/fuzz_hci_layer.h"
+#include "fuzz/helpers.h"
+
+namespace bluetooth {
+namespace hci {
+namespace fuzz {
+
+using bluetooth::common::ContextualCallback;
+using bluetooth::fuzz::GetArbitraryBytes;
+using bluetooth::fuzz::InvokeIfValid;
+
+hci::SecurityInterface* FuzzHciLayer::GetSecurityInterface(
+    ContextualCallback<void(hci::EventPacketView)> event_handler) {
+  return &security_interface_;
+}
+
+hci::LeSecurityInterface* FuzzHciLayer::GetLeSecurityInterface(
+    ContextualCallback<void(hci::LeMetaEventView)> event_handler) {
+  return &le_security_interface_;
+}
+
+hci::AclConnectionInterface* FuzzHciLayer::GetAclConnectionInterface(
+    ContextualCallback<void(hci::EventPacketView)> event_handler,
+    ContextualCallback<void(uint16_t, hci::ErrorCode)> on_disconnect) {
+  return &acl_connection_interface_;
+}
+
+hci::LeAclConnectionInterface* FuzzHciLayer::GetLeAclConnectionInterface(
+    ContextualCallback<void(hci::LeMetaEventView)> event_handler,
+    ContextualCallback<void(uint16_t, hci::ErrorCode)> on_disconnect) {
+  return &le_acl_connection_interface_;
+}
+
+hci::LeAdvertisingInterface* FuzzHciLayer::GetLeAdvertisingInterface(
+    ContextualCallback<void(hci::LeMetaEventView)> event_handler) {
+  return &le_advertising_interface_;
+}
+
+hci::LeScanningInterface* FuzzHciLayer::GetLeScanningInterface(
+    ContextualCallback<void(hci::LeMetaEventView)> event_handler) {
+  return &le_scanning_interface_;
+}
+
+void FuzzHciLayer::Start() {
+  acl_dev_null_ = new os::fuzz::DevNullQueue<AclPacketBuilder>(acl_queue_.GetDownEnd(), GetHandler());
+  acl_dev_null_->Start();
+  acl_inject_ = new os::fuzz::FuzzInjectQueue<AclPacketView>(acl_queue_.GetDownEnd(), GetHandler());
+}
+
+void FuzzHciLayer::Stop() {
+  acl_dev_null_->Stop();
+  delete acl_dev_null_;
+  delete acl_inject_;
+}
+
+void FuzzHciLayer::injectArbitrary(FuzzedDataProvider& fdp) {
+  const uint8_t action = fdp.ConsumeIntegralInRange(0, 13);
+  switch (action) {
+    case 1:
+      injectAclData(GetArbitraryBytes(&fdp));
+      break;
+    case 2:
+      injectCommandComplete(GetArbitraryBytes(&fdp));
+      break;
+    case 3:
+      injectCommandStatus(GetArbitraryBytes(&fdp));
+      break;
+    case 4:
+      injectEvent(fdp);
+      break;
+    case 5:
+      injectLeEvent(fdp);
+      break;
+    case 6:
+      injectSecurityEvent(GetArbitraryBytes(&fdp));
+      break;
+    case 7:
+      injectLeSecurityEvent(GetArbitraryBytes(&fdp));
+      break;
+    case 8:
+      injectAclEvent(GetArbitraryBytes(&fdp));
+      break;
+    case 9:
+      injectAclDisconnect(fdp);
+      break;
+    case 10:
+      injectLeAclEvent(GetArbitraryBytes(&fdp));
+      break;
+    case 11:
+      injectLeAclDisconnect(fdp);
+      break;
+    case 12:
+      injectLeAdvertisingEvent(GetArbitraryBytes(&fdp));
+      break;
+    case 13:
+      injectLeScanningEvent(GetArbitraryBytes(&fdp));
+      break;
+  }
+}
+
+void FuzzHciLayer::injectAclData(std::vector<uint8_t> data) {
+  CONSTRUCT_VALID_UNIQUE_OTHERWISE_BAIL(hci::AclPacketView, packet, data);
+  acl_inject_->Inject(std::move(packet));
+}
+
+void FuzzHciLayer::injectCommandComplete(std::vector<uint8_t> data) {
+  InvokeIfValid<hci::CommandCompleteView>(std::move(on_command_complete_), data);
+}
+
+void FuzzHciLayer::injectCommandStatus(std::vector<uint8_t> data) {
+  InvokeIfValid<hci::CommandStatusView>(std::move(on_command_status_), data);
+}
+
+void FuzzHciLayer::injectEvent(FuzzedDataProvider& fdp) {
+  auto handler_pair = event_handlers_.find(static_cast<EventCode>(fdp.ConsumeIntegral<uint8_t>()));
+  if (handler_pair != event_handlers_.end()) {
+    InvokeIfValid<EventPacketView>(handler_pair->second, GetArbitraryBytes(&fdp));
+  }
+}
+
+void FuzzHciLayer::injectLeEvent(FuzzedDataProvider& fdp) {
+  auto handler_pair = le_event_handlers_.find(static_cast<SubeventCode>(fdp.ConsumeIntegral<uint8_t>()));
+  if (handler_pair != le_event_handlers_.end()) {
+    InvokeIfValid<LeMetaEventView>(handler_pair->second, GetArbitraryBytes(&fdp));
+  }
+}
+
+void FuzzHciLayer::injectSecurityEvent(std::vector<uint8_t> data) {
+  InvokeIfValid<EventPacketView>(security_event_handler_, data);
+}
+
+void FuzzHciLayer::injectLeSecurityEvent(std::vector<uint8_t> data) {
+  InvokeIfValid<LeMetaEventView>(le_security_event_handler_, data);
+}
+
+void FuzzHciLayer::injectAclEvent(std::vector<uint8_t> data) {
+  InvokeIfValid<EventPacketView>(acl_event_handler_, data);
+}
+
+void FuzzHciLayer::injectAclDisconnect(FuzzedDataProvider& fdp) {
+  acl_on_disconnect_.InvokeIfNotEmpty(fdp.ConsumeIntegral<uint16_t>(),
+                                      static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+}
+
+void FuzzHciLayer::injectLeAclEvent(std::vector<uint8_t> data) {
+  InvokeIfValid<LeMetaEventView>(le_acl_event_handler_, data);
+}
+
+void FuzzHciLayer::injectLeAclDisconnect(FuzzedDataProvider& fdp) {
+  le_acl_on_disconnect_.InvokeIfNotEmpty(fdp.ConsumeIntegral<uint16_t>(),
+                                         static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+}
+
+void FuzzHciLayer::injectLeAdvertisingEvent(std::vector<uint8_t> data) {
+  InvokeIfValid<LeMetaEventView>(le_advertising_event_handler_, data);
+}
+
+void FuzzHciLayer::injectLeScanningEvent(std::vector<uint8_t> data) {
+  InvokeIfValid<LeMetaEventView>(le_scanning_event_handler_, data);
+}
+
+const ModuleFactory FuzzHciLayer::Factory = ModuleFactory([]() { return new FuzzHciLayer(); });
+
+}  // namespace fuzz
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/fuzz/fuzz_hci_layer.h b/gd/hci/fuzz/fuzz_hci_layer.h
new file mode 100644
index 0000000..3c34178
--- /dev/null
+++ b/gd/hci/fuzz/fuzz_hci_layer.h
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "hci/command_interface.h"
+#include "hci/hci_layer.h"
+#include "os/fuzz/dev_null_queue.h"
+#include "os/fuzz/fuzz_inject_queue.h"
+#include "os/log.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "fuzz/helpers.h"
+
+namespace bluetooth {
+namespace hci {
+namespace fuzz {
+
+template <typename T>
+class FuzzCommandInterface : public CommandInterface<T> {
+ public:
+  void EnqueueCommand(std::unique_ptr<T> command,
+                      common::ContextualOnceCallback<void(hci::CommandCompleteView)> on_complete) override {}
+
+  void EnqueueCommand(std::unique_ptr<T> command,
+                      common::ContextualOnceCallback<void(hci::CommandStatusView)> on_status) override {}
+};
+
+class FuzzHciLayer : public HciLayer {
+ public:
+  void TurnOnAutoReply(FuzzedDataProvider* fdp) {
+    auto_reply_fdp = fdp;
+  }
+
+  void TurnOffAutoReply() {
+    auto_reply_fdp = nullptr;
+  }
+
+  void EnqueueCommand(std::unique_ptr<hci::CommandPacketBuilder> command,
+                      common::ContextualOnceCallback<void(hci::CommandCompleteView)> on_complete) override {
+    on_command_complete_ = std::move(on_complete);
+    if (auto_reply_fdp != nullptr) {
+      injectCommandComplete(bluetooth::fuzz::GetArbitraryBytes(auto_reply_fdp));
+    }
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::ContextualOnceCallback<void(hci::CommandStatusView)> on_status) override {
+    on_command_status_ = std::move(on_status);
+    if (auto_reply_fdp != nullptr) {
+      injectCommandStatus(bluetooth::fuzz::GetArbitraryBytes(auto_reply_fdp));
+    }
+  }
+
+  common::BidiQueueEnd<hci::AclPacketBuilder, hci::AclPacketView>* GetAclQueueEnd() override {
+    return acl_queue_.GetUpEnd();
+  }
+
+  void RegisterEventHandler(hci::EventCode event,
+                            common::ContextualCallback<void(hci::EventPacketView)> handler) override {
+    event_handlers_[event] = handler;
+  }
+
+  void UnregisterEventHandler(hci::EventCode event) override {
+    auto it = event_handlers_.find(event);
+    if (it != event_handlers_.end()) {
+      event_handlers_.erase(it);
+    }
+  }
+
+  void RegisterLeEventHandler(hci::SubeventCode event,
+                              common::ContextualCallback<void(hci::LeMetaEventView)> handler) override {
+    le_event_handlers_[event] = handler;
+  }
+
+  void UnregisterLeEventHandler(hci::SubeventCode event) override {
+    auto it = le_event_handlers_.find(event);
+    if (it != le_event_handlers_.end()) {
+      le_event_handlers_.erase(it);
+    }
+  }
+
+  hci::SecurityInterface* GetSecurityInterface(
+      common::ContextualCallback<void(hci::EventPacketView)> event_handler) override;
+
+  hci::LeSecurityInterface* GetLeSecurityInterface(
+      common::ContextualCallback<void(hci::LeMetaEventView)> event_handler) override;
+
+  hci::AclConnectionInterface* GetAclConnectionInterface(
+      common::ContextualCallback<void(hci::EventPacketView)> event_handler,
+      common::ContextualCallback<void(uint16_t, hci::ErrorCode)> on_disconnect) override;
+
+  hci::LeAclConnectionInterface* GetLeAclConnectionInterface(
+      common::ContextualCallback<void(hci::LeMetaEventView)> event_handler,
+      common::ContextualCallback<void(uint16_t, hci::ErrorCode)> on_disconnect) override;
+
+  hci::LeAdvertisingInterface* GetLeAdvertisingInterface(
+      common::ContextualCallback<void(hci::LeMetaEventView)> event_handler) override;
+
+  hci::LeScanningInterface* GetLeScanningInterface(
+      common::ContextualCallback<void(hci::LeMetaEventView)> event_handler) override;
+
+  void injectArbitrary(FuzzedDataProvider& fdp);
+
+  std::string ToString() const override {
+    return "FuzzHciLayer";
+  }
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override;
+  void Stop() override;
+
+ private:
+  void injectAclData(std::vector<uint8_t> data);
+
+  void injectCommandComplete(std::vector<uint8_t> data);
+  void injectCommandStatus(std::vector<uint8_t> data);
+
+  void injectEvent(FuzzedDataProvider& fdp);
+  void injectLeEvent(FuzzedDataProvider& fdp);
+
+  void injectSecurityEvent(std::vector<uint8_t> data);
+  void injectLeSecurityEvent(std::vector<uint8_t> data);
+
+  void injectAclEvent(std::vector<uint8_t> data);
+  void injectAclDisconnect(FuzzedDataProvider& fdp);
+  void injectLeAclEvent(std::vector<uint8_t> data);
+  void injectLeAclDisconnect(FuzzedDataProvider& fdp);
+
+  void injectLeAdvertisingEvent(std::vector<uint8_t> data);
+
+  void injectLeScanningEvent(std::vector<uint8_t> data);
+
+  FuzzedDataProvider* auto_reply_fdp;
+
+  common::BidiQueue<hci::AclPacketView, hci::AclPacketBuilder> acl_queue_{3};
+  os::fuzz::DevNullQueue<AclPacketBuilder>* acl_dev_null_;
+  os::fuzz::FuzzInjectQueue<AclPacketView>* acl_inject_;
+
+  FuzzCommandInterface<ConnectionManagementCommandBuilder> acl_connection_interface_{};
+  FuzzCommandInterface<LeConnectionManagementCommandBuilder> le_acl_connection_interface_{};
+  FuzzCommandInterface<SecurityCommandBuilder> security_interface_{};
+  FuzzCommandInterface<LeSecurityCommandBuilder> le_security_interface_{};
+  FuzzCommandInterface<LeAdvertisingCommandBuilder> le_advertising_interface_{};
+  FuzzCommandInterface<LeScanningCommandBuilder> le_scanning_interface_{};
+
+  common::ContextualOnceCallback<void(hci::CommandCompleteView)> on_command_complete_;
+  common::ContextualOnceCallback<void(hci::CommandStatusView)> on_command_status_;
+
+  std::map<hci::EventCode, common::ContextualCallback<void(hci::EventPacketView)>> event_handlers_;
+  std::map<hci::SubeventCode, common::ContextualCallback<void(hci::LeMetaEventView)>> le_event_handlers_;
+
+  common::ContextualCallback<void(hci::EventPacketView)> security_event_handler_;
+  common::ContextualCallback<void(hci::LeMetaEventView)> le_security_event_handler_;
+  common::ContextualCallback<void(hci::EventPacketView)> acl_event_handler_;
+  common::ContextualCallback<void(uint16_t, hci::ErrorCode)> acl_on_disconnect_;
+  common::ContextualCallback<void(hci::LeMetaEventView)> le_acl_event_handler_;
+  common::ContextualCallback<void(uint16_t, hci::ErrorCode)> le_acl_on_disconnect_;
+  common::ContextualCallback<void(hci::LeMetaEventView)> le_advertising_event_handler_;
+  common::ContextualCallback<void(hci::LeMetaEventView)> le_scanning_event_handler_;
+};
+
+}  // namespace fuzz
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/fuzz/hci_layer_fuzz_client.cc b/gd/hci/fuzz/hci_layer_fuzz_client.cc
new file mode 100644
index 0000000..818e924
--- /dev/null
+++ b/gd/hci/fuzz/hci_layer_fuzz_client.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/fuzz/hci_layer_fuzz_client.h"
+#include "fuzz/helpers.h"
+
+namespace bluetooth {
+namespace hci {
+namespace fuzz {
+using bluetooth::fuzz::GetArbitraryBytes;
+using bluetooth::hci::AclPacketView;
+
+const ModuleFactory HciLayerFuzzClient::Factory = ModuleFactory([]() { return new HciLayerFuzzClient(); });
+
+void HciLayerFuzzClient::Start() {
+  hci_ = GetDependency<hci::HciLayer>();
+  aclDevNull_ = new os::fuzz::DevNullQueue<AclPacketView>(hci_->GetAclQueueEnd(), GetHandler());
+  aclDevNull_->Start();
+  aclInject_ = new os::fuzz::FuzzInjectQueue<AclPacketBuilder>(hci_->GetAclQueueEnd(), GetHandler());
+
+  // Can't do security right now, due to the Encryption Change conflict between ACL manager & security
+  // security_interface_ = hci_->GetSecurityInterface(common::Bind([](EventPacketView){}), GetHandler());
+  le_security_interface_ = hci_->GetLeSecurityInterface(GetHandler()->Bind([](LeMetaEventView) {}));
+  acl_connection_interface_ = hci_->GetAclConnectionInterface(GetHandler()->Bind([](EventPacketView) {}),
+                                                              GetHandler()->Bind([](uint16_t, hci::ErrorCode) {}));
+  le_acl_connection_interface_ = hci_->GetLeAclConnectionInterface(GetHandler()->Bind([](LeMetaEventView) {}),
+                                                                   GetHandler()->Bind([](uint16_t, hci::ErrorCode) {}));
+  le_advertising_interface_ = hci_->GetLeAdvertisingInterface(GetHandler()->Bind([](LeMetaEventView) {}));
+  le_scanning_interface_ = hci_->GetLeScanningInterface(GetHandler()->Bind([](LeMetaEventView) {}));
+}
+
+void HciLayerFuzzClient::Stop() {
+  aclDevNull_->Stop();
+  delete aclDevNull_;
+  delete aclInject_;
+}
+
+void HciLayerFuzzClient::injectArbitrary(FuzzedDataProvider& fdp) {
+  const uint8_t action = fdp.ConsumeIntegralInRange(0, 8);
+  switch (action) {
+    case 1:
+      injectAclData(GetArbitraryBytes(&fdp));
+      break;
+    case 2:
+      injectHciCommand(GetArbitraryBytes(&fdp));
+      break;
+    case 3:
+      // TODO: injectSecurityCommand(GetArbitraryBytes(&fdp));
+      break;
+    case 4:
+      injectLeSecurityCommand(GetArbitraryBytes(&fdp));
+      break;
+    case 5:
+      injectAclConnectionCommand(GetArbitraryBytes(&fdp));
+      break;
+    case 6:
+      injectLeAclConnectionCommand(GetArbitraryBytes(&fdp));
+      break;
+    case 7:
+      injectLeAdvertisingCommand(GetArbitraryBytes(&fdp));
+      break;
+    case 8:
+      injectLeScanningCommand(GetArbitraryBytes(&fdp));
+      break;
+  }
+}
+
+void HciLayerFuzzClient::injectAclData(std::vector<uint8_t> data) {
+  hci::AclPacketView aclPacket = hci::AclPacketView::FromBytes(data);
+  if (!aclPacket.IsValid()) {
+    return;
+  }
+
+  aclInject_->Inject(AclPacketBuilder::FromView(aclPacket));
+}
+
+void HciLayerFuzzClient::injectHciCommand(std::vector<uint8_t> data) {
+  inject_command<CommandPacketView, CommandPacketBuilder>(data, hci_);
+}
+
+void HciLayerFuzzClient::injectSecurityCommand(std::vector<uint8_t> data) {
+  inject_command<SecurityCommandView, SecurityCommandBuilder>(data, security_interface_);
+}
+
+void HciLayerFuzzClient::injectLeSecurityCommand(std::vector<uint8_t> data) {
+  inject_command<LeSecurityCommandView, LeSecurityCommandBuilder>(data, le_security_interface_);
+}
+
+void HciLayerFuzzClient::injectAclConnectionCommand(std::vector<uint8_t> data) {
+  inject_command<ConnectionManagementCommandView, ConnectionManagementCommandBuilder>(data, acl_connection_interface_);
+}
+
+void HciLayerFuzzClient::injectLeAclConnectionCommand(std::vector<uint8_t> data) {
+  inject_command<LeConnectionManagementCommandView, LeConnectionManagementCommandBuilder>(data,
+                                                                                          le_acl_connection_interface_);
+}
+
+void HciLayerFuzzClient::injectLeAdvertisingCommand(std::vector<uint8_t> data) {
+  inject_command<LeAdvertisingCommandView, LeAdvertisingCommandBuilder>(data, le_advertising_interface_);
+}
+
+void HciLayerFuzzClient::injectLeScanningCommand(std::vector<uint8_t> data) {
+  inject_command<LeScanningCommandView, LeScanningCommandBuilder>(data, le_scanning_interface_);
+}
+
+}  // namespace fuzz
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/fuzz/hci_layer_fuzz_client.h b/gd/hci/fuzz/hci_layer_fuzz_client.h
new file mode 100644
index 0000000..bc3861c
--- /dev/null
+++ b/gd/hci/fuzz/hci_layer_fuzz_client.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+#include "hci/fuzz/status_vs_complete_commands.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "os/fuzz/dev_null_queue.h"
+#include "os/fuzz/fuzz_inject_queue.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace bluetooth {
+namespace hci {
+namespace fuzz {
+
+class HciLayerFuzzClient : public Module {
+ public:
+  HciLayerFuzzClient() : Module() {}
+
+  void Start() override;
+  void Stop() override;
+
+  void injectArbitrary(FuzzedDataProvider& fdp);
+
+  void ListDependencies(ModuleList* list) override {
+    list->add<hci::HciLayer>();
+  }
+
+  static const ModuleFactory Factory;
+
+  std::string ToString() const override {
+    return "DevNullHci";
+  }
+
+ private:
+  void injectAclData(std::vector<uint8_t> data);
+  void injectHciCommand(std::vector<uint8_t> data);
+  void injectSecurityCommand(std::vector<uint8_t> data);
+  void injectLeSecurityCommand(std::vector<uint8_t> data);
+  void injectAclConnectionCommand(std::vector<uint8_t> data);
+  void injectLeAclConnectionCommand(std::vector<uint8_t> data);
+  void injectLeAdvertisingCommand(std::vector<uint8_t> data);
+  void injectLeScanningCommand(std::vector<uint8_t> data);
+
+  template <typename TVIEW, typename TBUILDER>
+  void inject_command(std::vector<uint8_t> data, CommandInterface<TBUILDER>* interface) {
+    TVIEW commandPacket = TVIEW::FromBytes(data);
+    if (!commandPacket.IsValid()) {
+      return;
+    }
+
+    if (uses_command_status(commandPacket.GetOpCode())) {
+      interface->EnqueueCommand(TBUILDER::FromView(commandPacket),
+                                GetHandler()->BindOnce([](CommandStatusView status) {}));
+    } else {
+      interface->EnqueueCommand(TBUILDER::FromView(commandPacket),
+                                GetHandler()->BindOnce([](CommandCompleteView status) {}));
+    }
+  }
+
+  hci::HciLayer* hci_ = nullptr;
+  os::fuzz::DevNullQueue<AclPacketView>* aclDevNull_;
+  os::fuzz::FuzzInjectQueue<AclPacketBuilder>* aclInject_;
+
+  SecurityInterface* security_interface_;
+  LeSecurityInterface* le_security_interface_;
+  AclConnectionInterface* acl_connection_interface_;
+  LeAclConnectionInterface* le_acl_connection_interface_;
+  LeAdvertisingInterface* le_advertising_interface_;
+  LeScanningInterface* le_scanning_interface_;
+};
+
+}  // namespace fuzz
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/fuzz/hci_layer_fuzz_test.cc b/gd/hci/fuzz/hci_layer_fuzz_test.cc
new file mode 100644
index 0000000..e6e27d8
--- /dev/null
+++ b/gd/hci/fuzz/hci_layer_fuzz_test.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include "fuzz/helpers.h"
+#include "hal/fuzz/fuzz_hci_hal.h"
+#include "hci/fuzz/hci_layer_fuzz_client.h"
+#include "hci/hci_layer.h"
+#include "module.h"
+#include "os/fuzz/fake_timerfd.h"
+#include "os/log.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using bluetooth::FuzzTestModuleRegistry;
+using bluetooth::fuzz::GetArbitraryBytes;
+using bluetooth::hal::HciHal;
+using bluetooth::hal::fuzz::FuzzHciHal;
+using bluetooth::hci::fuzz::HciLayerFuzzClient;
+using bluetooth::os::fuzz::fake_timerfd_advance;
+using bluetooth::os::fuzz::fake_timerfd_cap_at;
+using bluetooth::os::fuzz::fake_timerfd_reset;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider dataProvider(data, size);
+  fake_timerfd_cap_at(1999);  // prevent command timeouts
+
+  static FuzzTestModuleRegistry moduleRegistry = FuzzTestModuleRegistry();
+  FuzzHciHal* fuzzHal = moduleRegistry.Inject<FuzzHciHal>(&HciHal::Factory);
+  HciLayerFuzzClient* fuzzClient = moduleRegistry.Start<HciLayerFuzzClient>();
+
+  while (dataProvider.remaining_bytes() > 0) {
+    const uint8_t action = dataProvider.ConsumeIntegralInRange(0, 5);
+    switch (action) {
+      case 1:
+        fake_timerfd_advance(dataProvider.ConsumeIntegral<uint64_t>());
+        break;
+      case 2:
+        fuzzHal->injectArbitrary(dataProvider);
+        break;
+      case 3:
+        fuzzClient->injectArbitrary(dataProvider);
+        break;
+    }
+  }
+
+  moduleRegistry.WaitForIdleAndStopAll();
+  fake_timerfd_reset();
+  return 0;
+}
diff --git a/gd/hci/fuzz/status_vs_complete_commands.cc b/gd/hci/fuzz/status_vs_complete_commands.cc
new file mode 100644
index 0000000..cf4a5ff
--- /dev/null
+++ b/gd/hci/fuzz/status_vs_complete_commands.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/fuzz/status_vs_complete_commands.h"
+#include <map>
+
+namespace bluetooth {
+namespace hci {
+namespace fuzz {
+
+using ::bluetooth::hci::OpCode;
+
+constexpr OpCode StatusOpCodes[] = {
+    OpCode::RESET,
+};
+
+static std::map<OpCode, bool> commands_that_use_status;
+
+static void maybe_populate_list() {
+  if (!commands_that_use_status.empty()) {
+    return;
+  }
+
+  for (OpCode code : StatusOpCodes) {
+    commands_that_use_status[code] = true;
+  }
+}
+
+bool uses_command_status(OpCode code) {
+  maybe_populate_list();
+  return commands_that_use_status.find(code) != commands_that_use_status.end();
+}
+
+}  // namespace fuzz
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/l2cap/security_policy.h b/gd/hci/fuzz/status_vs_complete_commands.h
similarity index 83%
rename from gd/l2cap/security_policy.h
rename to gd/hci/fuzz/status_vs_complete_commands.h
index 5a06401..c50ce5e 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/hci/fuzz/status_vs_complete_commands.h
@@ -13,12 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
+#include "hci/hci_packets.h"
+
 namespace bluetooth {
-namespace l2cap {
+namespace hci {
+namespace fuzz {
 
-class SecurityPolicy {};
+bool uses_command_status(hci::OpCode code);
 
-}  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/hci_layer.cc b/gd/hci/hci_layer.cc
index 5d903de..5b68cb0 100644
--- a/gd/hci/hci_layer.cc
+++ b/gd/hci/hci_layer.cc
@@ -17,329 +17,133 @@
 #include "hci/hci_layer.h"
 
 #include "common/bind.h"
-#include "common/callback.h"
 #include "os/alarm.h"
 #include "os/queue.h"
 #include "packet/packet_builder.h"
 
-namespace {
-using bluetooth::common::Bind;
+namespace bluetooth {
+namespace hci {
+using bluetooth::common::BindOn;
 using bluetooth::common::BindOnce;
-using bluetooth::common::Callback;
-using bluetooth::common::Closure;
-using bluetooth::common::OnceCallback;
-using bluetooth::common::OnceClosure;
+using bluetooth::common::ContextualCallback;
+using bluetooth::common::ContextualOnceCallback;
 using bluetooth::hci::CommandCompleteView;
 using bluetooth::hci::CommandPacketBuilder;
 using bluetooth::hci::CommandStatusView;
 using bluetooth::hci::EventPacketView;
 using bluetooth::hci::LeMetaEventView;
 using bluetooth::os::Handler;
-
-class EventHandler {
- public:
-  EventHandler() : event_handler(), handler(nullptr) {}
-  EventHandler(Callback<void(EventPacketView)> on_event, Handler* on_event_handler)
-      : event_handler(std::move(on_event)), handler(on_event_handler) {}
-  Callback<void(EventPacketView)> event_handler;
-  Handler* handler;
-};
-
-class SubeventHandler {
- public:
-  SubeventHandler() : subevent_handler(), handler(nullptr) {}
-  SubeventHandler(Callback<void(LeMetaEventView)> on_event, Handler* on_event_handler)
-      : subevent_handler(std::move(on_event)), handler(on_event_handler) {}
-  Callback<void(LeMetaEventView)> subevent_handler;
-  Handler* handler;
-};
-
-class CommandQueueEntry {
- public:
-  CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
-                    OnceCallback<void(CommandCompleteView)> on_complete_function, Handler* handler)
-      : command(std::move(command_packet)), waiting_for_status_(false), on_complete(std::move(on_complete_function)),
-        caller_handler(handler) {}
-
-  CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
-                    OnceCallback<void(CommandStatusView)> on_status_function, Handler* handler)
-      : command(std::move(command_packet)), waiting_for_status_(true), on_status(std::move(on_status_function)),
-        caller_handler(handler) {}
-
-  std::unique_ptr<CommandPacketBuilder> command;
-  bool waiting_for_status_;
-  OnceCallback<void(CommandStatusView)> on_status;
-  OnceCallback<void(CommandCompleteView)> on_complete;
-  Handler* caller_handler;
-};
-}  // namespace
-
-namespace bluetooth {
-namespace hci {
-
 using common::BidiQueue;
 using common::BidiQueueEnd;
-using os::Alarm;
-using os::Handler;
-
-namespace {
 using hci::OpCode;
 using hci::ResetCompleteView;
+using os::Alarm;
+using os::Handler;
+using std::move;
+using std::unique_ptr;
 
-void fail_if_reset_complete_not_success(CommandCompleteView complete) {
+static void fail_if_reset_complete_not_success(CommandCompleteView complete) {
   auto reset_complete = ResetCompleteView::Create(complete);
   ASSERT(reset_complete.IsValid());
   ASSERT(reset_complete.GetStatus() == ErrorCode::SUCCESS);
 }
 
-void on_hci_timeout(OpCode op_code) {
+static void on_hci_timeout(OpCode op_code) {
   ASSERT_LOG(false, "Timed out waiting for 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str());
 }
-}  // namespace
 
-class SecurityInterfaceImpl : public SecurityInterface {
+class CommandQueueEntry {
  public:
-  SecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {}
-  virtual ~SecurityInterfaceImpl() = default;
+  CommandQueueEntry(unique_ptr<CommandPacketBuilder> command_packet,
+                    ContextualOnceCallback<void(CommandCompleteView)> on_complete_function)
+      : command(move(command_packet)), waiting_for_status_(false), on_complete(move(on_complete_function)) {}
 
-  virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete,
-                              os::Handler* handler) override {
-    hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
+  CommandQueueEntry(unique_ptr<CommandPacketBuilder> command_packet,
+                    ContextualOnceCallback<void(CommandStatusView)> on_status_function)
+      : command(move(command_packet)), waiting_for_status_(true), on_status(move(on_status_function)) {}
+
+  unique_ptr<CommandPacketBuilder> command;
+  bool waiting_for_status_;
+  ContextualOnceCallback<void(CommandStatusView)> on_status;
+  ContextualOnceCallback<void(CommandCompleteView)> on_complete;
+
+  template <typename TView>
+  ContextualOnceCallback<void(TView)>* GetCallback() {
+    return nullptr;
   }
 
-  virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
-    hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
+  template <>
+  ContextualOnceCallback<void(CommandStatusView)>* GetCallback<CommandStatusView>() {
+    return &on_status;
   }
-  HciLayer& hci_;
+
+  template <>
+  ContextualOnceCallback<void(CommandCompleteView)>* GetCallback<CommandCompleteView>() {
+    return &on_complete;
+  }
 };
 
-class LeSecurityInterfaceImpl : public LeSecurityInterface {
- public:
-  LeSecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {}
-  virtual ~LeSecurityInterfaceImpl() = default;
-
-  virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete,
-                              os::Handler* handler) override {
-    hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
+struct HciLayer::impl {
+  impl(hal::HciHal* hal, HciLayer& module) : hal_(hal), module_(module) {
+    hci_timeout_alarm_ = new Alarm(module.GetHandler());
   }
 
-  virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
-    hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
-  }
-  HciLayer& hci_;
-};
-
-class LeAdvertisingInterfaceImpl : public LeAdvertisingInterface {
- public:
-  LeAdvertisingInterfaceImpl(HciLayer& hci) : hci_(hci) {}
-  virtual ~LeAdvertisingInterfaceImpl() = default;
-
-  virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete,
-                              os::Handler* handler) override {
-    hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
-  }
-
-  virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
-    hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
-  }
-  HciLayer& hci_;
-};
-
-class LeScanningInterfaceImpl : public LeScanningInterface {
- public:
-  LeScanningInterfaceImpl(HciLayer& hci) : hci_(hci) {}
-  virtual ~LeScanningInterfaceImpl() = default;
-
-  virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete,
-                              os::Handler* handler) override {
-    hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
-  }
-
-  virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
-    hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
-  }
-  HciLayer& hci_;
-};
-
-struct HciLayer::impl : public hal::HciHalCallbacks {
-  impl(HciLayer& module) : hal_(nullptr), module_(module) {}
-
-  ~impl() {}
-
-  void Start(hal::HciHal* hal) {
-    hal_ = hal;
-    hci_timeout_alarm_ = new Alarm(module_.GetHandler());
-
-    auto queue_end = acl_queue_.GetDownEnd();
-    Handler* handler = module_.GetHandler();
-    queue_end->RegisterDequeue(handler, Bind(&impl::dequeue_and_send_acl, common::Unretained(this)));
-    RegisterEventHandler(EventCode::COMMAND_COMPLETE, Bind(&impl::command_complete_callback, common::Unretained(this)),
-                         handler);
-    RegisterEventHandler(EventCode::COMMAND_STATUS, Bind(&impl::command_status_callback, common::Unretained(this)),
-                         handler);
-    RegisterEventHandler(EventCode::LE_META_EVENT, Bind(&impl::le_meta_event_callback, common::Unretained(this)),
-                         handler);
-    // TODO find the right place
-    RegisterEventHandler(EventCode::PAGE_SCAN_REPETITION_MODE_CHANGE, Bind(&impl::drop, common::Unretained(this)),
-                         handler);
-    RegisterEventHandler(EventCode::MAX_SLOTS_CHANGE, Bind(&impl::drop, common::Unretained(this)), handler);
-    RegisterEventHandler(EventCode::VENDOR_SPECIFIC, Bind(&impl::drop, common::Unretained(this)), handler);
-
-    EnqueueCommand(ResetBuilder::Create(), BindOnce(&fail_if_reset_complete_not_success), handler);
-    hal_->registerIncomingPacketCallback(this);
+  ~impl() {
+    incoming_acl_buffer_.Clear();
+    delete hci_timeout_alarm_;
+    command_queue_.clear();
   }
 
   void drop(EventPacketView) {}
 
-  void dequeue_and_send_acl() {
+  void on_outbound_acl_ready() {
     auto packet = acl_queue_.GetDownEnd()->TryDequeue();
-    send_acl(std::move(packet));
-  }
-
-  void Stop() {
-    hal_->unregisterIncomingPacketCallback();
-    acl_queue_.GetDownEnd()->UnregisterDequeue();
-    incoming_acl_packet_buffer_.Clear();
-    delete hci_timeout_alarm_;
-    command_queue_.clear();
-    hal_ = nullptr;
-  }
-
-  void send_acl(std::unique_ptr<hci::BasePacketBuilder> packet) {
     std::vector<uint8_t> bytes;
     BitInserter bi(bytes);
     packet->Serialize(bi);
     hal_->sendAclData(bytes);
   }
 
-  void send_sco(std::unique_ptr<hci::BasePacketBuilder> packet) {
-    std::vector<uint8_t> bytes;
-    BitInserter bi(bytes);
-    packet->Serialize(bi);
-    hal_->sendScoData(bytes);
+  template <typename TResponse>
+  void enqueue_command(unique_ptr<CommandPacketBuilder> command, ContextualOnceCallback<void(TResponse)> on_response) {
+    command_queue_.emplace_back(move(command), move(on_response));
+    send_next_command();
   }
 
-  void command_status_callback(EventPacketView event) {
-    CommandStatusView status_view = CommandStatusView::Create(event);
-    ASSERT(status_view.IsValid());
-    command_credits_ = status_view.GetNumHciCommandPackets();
-    OpCode op_code = status_view.GetCommandOpCode();
+  void on_command_status(EventPacketView event) {
+    handle_command_response<CommandStatusView>(event, "status");
+  }
+
+  void on_command_complete(EventPacketView event) {
+    handle_command_response<CommandCompleteView>(event, "complete");
+  }
+
+  template <typename TResponse>
+  void handle_command_response(EventPacketView event, std::string logging_id) {
+    TResponse response_view = TResponse::Create(event);
+    ASSERT(response_view.IsValid());
+    command_credits_ = response_view.GetNumHciCommandPackets();
+    OpCode op_code = response_view.GetCommandOpCode();
     if (op_code == OpCode::NONE) {
       send_next_command();
       return;
     }
-    ASSERT_LOG(!command_queue_.empty(), "Unexpected status event with OpCode 0x%02hx (%s)", op_code,
+    bool is_status = logging_id == "status";
+
+    ASSERT_LOG(!command_queue_.empty(), "Unexpected %s event with OpCode 0x%02hx (%s)", logging_id.c_str(), op_code,
                OpCodeText(op_code).c_str());
     ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_,
                OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str());
-    ASSERT_LOG(command_queue_.front().waiting_for_status_,
-               "Waiting for command complete 0x%02hx (%s), got command status for 0x%02hx (%s)", waiting_command_,
-               OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str());
-    auto caller_handler = command_queue_.front().caller_handler;
-    caller_handler->Post(BindOnce(std::move(command_queue_.front().on_status), std::move(status_view)));
+    ASSERT_LOG(command_queue_.front().waiting_for_status_ == is_status, "0x%02hx (%s) was not expecting %s event",
+               op_code, OpCodeText(op_code).c_str(), logging_id.c_str());
+
+    command_queue_.front().GetCallback<TResponse>()->Invoke(move(response_view));
     command_queue_.pop_front();
     waiting_command_ = OpCode::NONE;
     hci_timeout_alarm_->Cancel();
     send_next_command();
   }
 
-  void command_complete_callback(EventPacketView event) {
-    CommandCompleteView complete_view = CommandCompleteView::Create(event);
-    ASSERT(complete_view.IsValid());
-    command_credits_ = complete_view.GetNumHciCommandPackets();
-    OpCode op_code = complete_view.GetCommandOpCode();
-    if (op_code == OpCode::NONE) {
-      send_next_command();
-      return;
-    }
-    ASSERT_LOG(command_queue_.size() > 0, "Unexpected command complete with OpCode 0x%02hx (%s)", op_code,
-               OpCodeText(op_code).c_str());
-    ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_,
-               OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str());
-    ASSERT_LOG(!command_queue_.front().waiting_for_status_,
-               "Waiting for command status 0x%02hx (%s), got command complete for 0x%02hx (%s)", waiting_command_,
-               OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str());
-    auto caller_handler = command_queue_.front().caller_handler;
-    caller_handler->Post(BindOnce(std::move(command_queue_.front().on_complete), complete_view));
-    command_queue_.pop_front();
-    waiting_command_ = OpCode::NONE;
-    hci_timeout_alarm_->Cancel();
-    send_next_command();
-  }
-
-  void le_meta_event_callback(EventPacketView event) {
-    LeMetaEventView meta_event_view = LeMetaEventView::Create(event);
-    ASSERT(meta_event_view.IsValid());
-    SubeventCode subevent_code = meta_event_view.GetSubeventCode();
-    ASSERT_LOG(subevent_handlers_.find(subevent_code) != subevent_handlers_.end(),
-               "Unhandled le event of type 0x%02hhx (%s)", subevent_code, SubeventCodeText(subevent_code).c_str());
-    auto& registered_handler = subevent_handlers_[subevent_code].subevent_handler;
-    subevent_handlers_[subevent_code].handler->Post(BindOnce(registered_handler, meta_event_view));
-  }
-
-  void hciEventReceived(hal::HciPacket event_bytes) override {
-    auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(event_bytes));
-    EventPacketView event = EventPacketView::Create(packet);
-    ASSERT(event.IsValid());
-    module_.GetHandler()->Post(
-        BindOnce(&HciLayer::impl::hci_event_received_handler, common::Unretained(this), std::move(event)));
-  }
-
-  void hci_event_received_handler(EventPacketView event) {
-    EventCode event_code = event.GetEventCode();
-    ASSERT_LOG(event_handlers_.find(event_code) != event_handlers_.end(), "Unhandled event of type 0x%02hhx (%s)",
-               event_code, EventCodeText(event_code).c_str());
-    auto& registered_handler = event_handlers_[event_code].event_handler;
-    event_handlers_[event_code].handler->Post(BindOnce(registered_handler, std::move(event)));
-  }
-
-  void aclDataReceived(hal::HciPacket data_bytes) override {
-    auto packet =
-        packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(std::move(data_bytes)));
-    AclPacketView acl = AclPacketView::Create(packet);
-    incoming_acl_packet_buffer_.Enqueue(std::make_unique<AclPacketView>(acl), module_.GetHandler());
-  }
-
-  void scoDataReceived(hal::HciPacket data_bytes) override {
-    auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(data_bytes));
-    ScoPacketView sco = ScoPacketView::Create(packet);
-  }
-
-  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) {
-    module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_complete, common::Unretained(this),
-                                                std::move(command), std::move(on_complete),
-                                                common::Unretained(handler)));
-  }
-
-  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status,
-                      os::Handler* handler) {
-    module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_status, common::Unretained(this),
-                                                std::move(command), std::move(on_status), common::Unretained(handler)));
-  }
-
-  void handle_enqueue_command_with_complete(std::unique_ptr<CommandPacketBuilder> command,
-                                            OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) {
-    command_queue_.emplace_back(std::move(command), std::move(on_complete), handler);
-
-    send_next_command();
-  }
-
-  void handle_enqueue_command_with_status(std::unique_ptr<CommandPacketBuilder> command,
-                                          OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) {
-    command_queue_.emplace_back(std::move(command), std::move(on_status), handler);
-
-    send_next_command();
-  }
-
   void send_next_command() {
     if (command_credits_ == 0) {
       return;
@@ -354,6 +158,7 @@
     BitInserter bi(*bytes);
     command_queue_.front().command->Serialize(bi);
     hal_->sendHciCommand(*bytes);
+
     auto cmd_view = CommandPacketView::Create(bytes);
     ASSERT(cmd_view.IsValid());
     OpCode op_code = cmd_view.GetOpCode();
@@ -362,150 +167,184 @@
     hci_timeout_alarm_->Schedule(BindOnce(&on_hci_timeout, op_code), kHciTimeoutMs);
   }
 
-  BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd() {
-    return acl_queue_.GetUpEnd();
+  void register_event(EventCode event, ContextualCallback<void(EventPacketView)> handler) {
+    ASSERT_LOG(event_handlers_.count(event) == 0, "Can not register a second handler for %02hhx (%s)", event,
+               EventCodeText(event).c_str());
+    event_handlers_[event] = handler;
   }
 
-  void RegisterEventHandler(EventCode event_code, Callback<void(EventPacketView)> event_handler, os::Handler* handler) {
-    module_.GetHandler()->Post(common::BindOnce(&impl::handle_register_event_handler, common::Unretained(this),
-                                                event_code, event_handler, common::Unretained(handler)));
+  void unregister_event(EventCode event) {
+    event_handlers_.erase(event_handlers_.find(event));
   }
 
-  void handle_register_event_handler(EventCode event_code, Callback<void(EventPacketView)> event_handler,
-                                     os::Handler* handler) {
-    ASSERT_LOG(event_handlers_.count(event_code) == 0, "Can not register a second handler for event_code %02hhx (%s)",
-               event_code, EventCodeText(event_code).c_str());
-    EventHandler to_save(event_handler, handler);
-    event_handlers_[event_code] = to_save;
+  void register_le_event(SubeventCode event, ContextualCallback<void(LeMetaEventView)> handler) {
+    ASSERT_LOG(subevent_handlers_.count(event) == 0, "Can not register a second handler for %02hhx (%s)", event,
+               SubeventCodeText(event).c_str());
+    subevent_handlers_[event] = handler;
   }
 
-  void UnregisterEventHandler(EventCode event_code) {
-    module_.GetHandler()->Post(
-        common::BindOnce(&impl::handle_unregister_event_handler, common::Unretained(this), event_code));
+  void unregister_le_event(SubeventCode event) {
+    subevent_handlers_.erase(subevent_handlers_.find(event));
   }
 
-  void handle_unregister_event_handler(EventCode event_code) {
-    event_handlers_.erase(event_code);
+  void on_hci_event(EventPacketView event) {
+    ASSERT(event.IsValid());
+    EventCode event_code = event.GetEventCode();
+    if (event_handlers_.find(event_code) == event_handlers_.end()) {
+      LOG_DEBUG("Dropping unregistered event of type 0x%02hhx (%s)", event_code, EventCodeText(event_code).c_str());
+      return;
+    }
+    event_handlers_[event_code].Invoke(event);
   }
 
-  void RegisterLeEventHandler(SubeventCode subevent_code, Callback<void(LeMetaEventView)> event_handler,
-                              os::Handler* handler) {
-    module_.GetHandler()->Post(common::BindOnce(&impl::handle_register_le_event_handler, common::Unretained(this),
-                                                subevent_code, event_handler, common::Unretained(handler)));
+  void on_le_meta_event(EventPacketView event) {
+    LeMetaEventView meta_event_view = LeMetaEventView::Create(event);
+    ASSERT(meta_event_view.IsValid());
+    SubeventCode subevent_code = meta_event_view.GetSubeventCode();
+    ASSERT_LOG(subevent_handlers_.find(subevent_code) != subevent_handlers_.end(),
+               "Unhandled le event of type 0x%02hhx (%s)", subevent_code, SubeventCodeText(subevent_code).c_str());
+    subevent_handlers_[subevent_code].Invoke(meta_event_view);
   }
 
-  void handle_register_le_event_handler(SubeventCode subevent_code, Callback<void(LeMetaEventView)> subevent_handler,
-                                        os::Handler* handler) {
-    ASSERT_LOG(subevent_handlers_.count(subevent_code) == 0,
-               "Can not register a second handler for subevent_code %02hhx (%s)", subevent_code,
-               SubeventCodeText(subevent_code).c_str());
-    SubeventHandler to_save(subevent_handler, handler);
-    subevent_handlers_[subevent_code] = to_save;
-  }
-
-  void UnregisterLeEventHandler(SubeventCode subevent_code) {
-    module_.GetHandler()->Post(
-        common::BindOnce(&impl::handle_unregister_le_event_handler, common::Unretained(this), subevent_code));
-  }
-
-  void handle_unregister_le_event_handler(SubeventCode subevent_code) {
-    subevent_handlers_.erase(subevent_code);
-  }
-
-  // The HAL
   hal::HciHal* hal_;
-
-  // A reference to the HciLayer module
   HciLayer& module_;
 
-  // Interfaces
-  SecurityInterfaceImpl security_interface{module_};
-  LeSecurityInterfaceImpl le_security_interface{module_};
-  LeAdvertisingInterfaceImpl le_advertising_interface{module_};
-  LeScanningInterfaceImpl le_scanning_interface{module_};
-
   // Command Handling
   std::list<CommandQueueEntry> command_queue_;
 
-  std::map<EventCode, EventHandler> event_handlers_;
-  std::map<SubeventCode, SubeventHandler> subevent_handlers_;
+  std::map<EventCode, ContextualCallback<void(EventPacketView)>> event_handlers_;
+  std::map<SubeventCode, ContextualCallback<void(LeMetaEventView)>> subevent_handlers_;
   OpCode waiting_command_{OpCode::NONE};
   uint8_t command_credits_{1};  // Send reset first
   Alarm* hci_timeout_alarm_{nullptr};
 
   // Acl packets
   BidiQueue<AclPacketView, AclPacketBuilder> acl_queue_{3 /* TODO: Set queue depth */};
-  os::EnqueueBuffer<AclPacketView> incoming_acl_packet_buffer_{acl_queue_.GetDownEnd()};
+  os::EnqueueBuffer<AclPacketView> incoming_acl_buffer_{acl_queue_.GetDownEnd()};
 };
 
-HciLayer::HciLayer() : impl_(std::make_unique<impl>(*this)) {}
+// All functions here are running on the HAL thread
+struct HciLayer::hal_callbacks : public hal::HciHalCallbacks {
+  hal_callbacks(HciLayer& module) : module_(module) {}
+
+  void hciEventReceived(hal::HciPacket event_bytes) override {
+    auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(event_bytes));
+    EventPacketView event = EventPacketView::Create(packet);
+    module_.CallOn(module_.impl_, &impl::on_hci_event, move(event));
+  }
+
+  void aclDataReceived(hal::HciPacket data_bytes) override {
+    auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(move(data_bytes)));
+    auto acl = std::make_unique<AclPacketView>(AclPacketView::Create(packet));
+    module_.impl_->incoming_acl_buffer_.Enqueue(move(acl), module_.GetHandler());
+  }
+
+  void scoDataReceived(hal::HciPacket data_bytes) override {
+    // Not implemented yet
+  }
+
+  HciLayer& module_;
+};
+
+HciLayer::HciLayer() : impl_(nullptr), hal_callbacks_(nullptr) {}
 
 HciLayer::~HciLayer() {
-  impl_.reset();
-}
-
-void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) {
-  impl_->EnqueueCommand(std::move(command), std::move(on_complete), handler);
-}
-
-void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) {
-  impl_->EnqueueCommand(std::move(command), std::move(on_status), handler);
 }
 
 common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* HciLayer::GetAclQueueEnd() {
-  return impl_->GetAclQueueEnd();
+  return impl_->acl_queue_.GetUpEnd();
 }
 
-void HciLayer::RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
-                                    os::Handler* handler) {
-  impl_->RegisterEventHandler(event_code, std::move(event_handler), handler);
+void HciLayer::EnqueueCommand(unique_ptr<CommandPacketBuilder> command,
+                              ContextualOnceCallback<void(CommandCompleteView)> on_complete) {
+  CallOn(impl_, &impl::enqueue_command<CommandCompleteView>, move(command), move(on_complete));
 }
 
-void HciLayer::UnregisterEventHandler(EventCode event_code) {
-  impl_->UnregisterEventHandler(event_code);
+void HciLayer::EnqueueCommand(unique_ptr<CommandPacketBuilder> command,
+                              ContextualOnceCallback<void(CommandStatusView)> on_status) {
+  CallOn(impl_, &impl::enqueue_command<CommandStatusView>, move(command), move(on_status));
 }
 
-void HciLayer::RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
-                                      os::Handler* handler) {
-  impl_->RegisterLeEventHandler(subevent_code, std::move(event_handler), handler);
+void HciLayer::RegisterEventHandler(EventCode event, ContextualCallback<void(EventPacketView)> handler) {
+  CallOn(impl_, &impl::register_event, event, handler);
 }
 
-void HciLayer::UnregisterLeEventHandler(SubeventCode subevent_code) {
-  impl_->UnregisterLeEventHandler(subevent_code);
+void HciLayer::UnregisterEventHandler(EventCode event) {
+  CallOn(impl_, &impl::unregister_event, event);
 }
 
-SecurityInterface* HciLayer::GetSecurityInterface(common::Callback<void(EventPacketView)> event_handler,
-                                                  os::Handler* handler) {
-  for (const auto event : SecurityInterface::SecurityEvents) {
-    RegisterEventHandler(event, event_handler, handler);
+void HciLayer::RegisterLeEventHandler(SubeventCode event, ContextualCallback<void(LeMetaEventView)> handler) {
+  CallOn(impl_, &impl::register_le_event, event, handler);
+}
+
+void HciLayer::UnregisterLeEventHandler(SubeventCode event) {
+  CallOn(impl_, &impl::unregister_le_event, event);
+}
+
+void HciLayer::on_disconnection_complete(EventPacketView event_view) {
+  auto disconnection_view = DisconnectionCompleteView::Create(event_view);
+  if (!disconnection_view.IsValid()) {
+    LOG_INFO("Dropping invalid disconnection packet");
+    return;
   }
-  return &impl_->security_interface;
+
+  uint16_t handle = disconnection_view.GetConnectionHandle();
+  ErrorCode reason = disconnection_view.GetReason();
+  Disconnect(handle, reason);
 }
 
-LeSecurityInterface* HciLayer::GetLeSecurityInterface(common::Callback<void(LeMetaEventView)> event_handler,
-                                                      os::Handler* handler) {
-  for (const auto subevent : LeSecurityInterface::LeSecurityEvents) {
-    RegisterLeEventHandler(subevent, event_handler, handler);
+void HciLayer::Disconnect(uint16_t handle, ErrorCode reason) {
+  for (auto callback : disconnect_handlers_) {
+    callback.Invoke(handle, reason);
   }
-  return &impl_->le_security_interface;
 }
 
-LeAdvertisingInterface* HciLayer::GetLeAdvertisingInterface(common::Callback<void(LeMetaEventView)> event_handler,
-                                                            os::Handler* handler) {
-  for (const auto subevent : LeAdvertisingInterface::LeAdvertisingEvents) {
-    RegisterLeEventHandler(subevent, event_handler, handler);
+AclConnectionInterface* HciLayer::GetAclConnectionInterface(
+    ContextualCallback<void(EventPacketView)> event_handler,
+    ContextualCallback<void(uint16_t, ErrorCode)> on_disconnect) {
+  for (const auto event : AclConnectionEvents) {
+    RegisterEventHandler(event, event_handler);
   }
-  return &impl_->le_advertising_interface;
+  disconnect_handlers_.push_back(on_disconnect);
+  return &acl_connection_manager_interface_;
 }
 
-LeScanningInterface* HciLayer::GetLeScanningInterface(common::Callback<void(LeMetaEventView)> event_handler,
-                                                      os::Handler* handler) {
-  for (const auto subevent : LeScanningInterface::LeScanningEvents) {
-    RegisterLeEventHandler(subevent, event_handler, handler);
+LeAclConnectionInterface* HciLayer::GetLeAclConnectionInterface(
+    ContextualCallback<void(LeMetaEventView)> event_handler,
+    ContextualCallback<void(uint16_t, ErrorCode)> on_disconnect) {
+  for (const auto event : LeConnectionManagementEvents) {
+    RegisterLeEventHandler(event, event_handler);
   }
-  return &impl_->le_scanning_interface;
+  disconnect_handlers_.push_back(on_disconnect);
+  return &le_acl_connection_manager_interface_;
+}
+
+SecurityInterface* HciLayer::GetSecurityInterface(ContextualCallback<void(EventPacketView)> event_handler) {
+  for (const auto event : SecurityEvents) {
+    RegisterEventHandler(event, event_handler);
+  }
+  return &security_interface;
+}
+
+LeSecurityInterface* HciLayer::GetLeSecurityInterface(ContextualCallback<void(LeMetaEventView)> event_handler) {
+  for (const auto subevent : LeSecurityEvents) {
+    RegisterLeEventHandler(subevent, event_handler);
+  }
+  return &le_security_interface;
+}
+
+LeAdvertisingInterface* HciLayer::GetLeAdvertisingInterface(ContextualCallback<void(LeMetaEventView)> event_handler) {
+  for (const auto subevent : LeAdvertisingEvents) {
+    RegisterLeEventHandler(subevent, event_handler);
+  }
+  return &le_advertising_interface;
+}
+
+LeScanningInterface* HciLayer::GetLeScanningInterface(ContextualCallback<void(LeMetaEventView)> event_handler) {
+  for (const auto subevent : LeScanningEvents) {
+    RegisterLeEventHandler(subevent, event_handler);
+  }
+  return &le_scanning_interface;
 }
 
 const ModuleFactory HciLayer::Factory = ModuleFactory([]() { return new HciLayer(); });
@@ -515,15 +354,34 @@
 }
 
 void HciLayer::Start() {
-  impl_->Start(GetDependency<hal::HciHal>());
+  auto hal = GetDependency<hal::HciHal>();
+  impl_ = new impl(hal, *this);
+  hal_callbacks_ = new hal_callbacks(*this);
+
+  Handler* handler = GetHandler();
+  impl_->acl_queue_.GetDownEnd()->RegisterDequeue(handler, BindOn(impl_, &impl::on_outbound_acl_ready));
+  RegisterEventHandler(EventCode::COMMAND_COMPLETE, handler->BindOn(impl_, &impl::on_command_complete));
+  RegisterEventHandler(EventCode::COMMAND_STATUS, handler->BindOn(impl_, &impl::on_command_status));
+  RegisterEventHandler(EventCode::LE_META_EVENT, handler->BindOn(impl_, &impl::on_le_meta_event));
+  RegisterEventHandler(EventCode::DISCONNECTION_COMPLETE, handler->BindOn(this, &HciLayer::on_disconnection_complete));
+  // TODO find the right place
+  auto drop_packet = handler->BindOn(impl_, &impl::drop);
+  RegisterEventHandler(EventCode::PAGE_SCAN_REPETITION_MODE_CHANGE, drop_packet);
+  RegisterEventHandler(EventCode::MAX_SLOTS_CHANGE, drop_packet);
+  RegisterEventHandler(EventCode::VENDOR_SPECIFIC, drop_packet);
+
+  EnqueueCommand(ResetBuilder::Create(), handler->BindOnce(&fail_if_reset_complete_not_success));
+  hal->registerIncomingPacketCallback(hal_callbacks_);
 }
 
 void HciLayer::Stop() {
-  impl_->Stop();
+  auto hal = GetDependency<hal::HciHal>();
+  hal->unregisterIncomingPacketCallback();
+  delete hal_callbacks_;
+
+  impl_->acl_queue_.GetDownEnd()->UnregisterDequeue();
+  delete impl_;
 }
 
-std::string HciLayer::ToString() const {
-  return "Hci Layer";
-}
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/hci_layer.h b/gd/hci/hci_layer.h
index 3e299d4..8123405 100644
--- a/gd/hci/hci_layer.h
+++ b/gd/hci/hci_layer.h
@@ -23,8 +23,11 @@
 #include "class_of_device.h"
 #include "common/bidi_queue.h"
 #include "common/callback.h"
+#include "common/contextual_callback.h"
 #include "hal/hci_hal.h"
+#include "hci/acl_connection_interface.h"
 #include "hci/hci_packets.h"
+#include "hci/le_acl_connection_interface.h"
 #include "hci/le_advertising_interface.h"
 #include "hci/le_scanning_interface.h"
 #include "hci/le_security_interface.h"
@@ -35,55 +38,100 @@
 namespace bluetooth {
 namespace hci {
 
-class HciLayer : public Module {
+class HciLayer : public Module, public CommandInterface<CommandPacketBuilder> {
+  // LINT.IfChange
  public:
   HciLayer();
   virtual ~HciLayer();
   DISALLOW_COPY_AND_ASSIGN(HciLayer);
 
-  virtual void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler);
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::ContextualOnceCallback<void(CommandCompleteView)> on_complete) override;
 
-  virtual void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler);
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::ContextualOnceCallback<void(CommandStatusView)> on_status) override;
 
   virtual common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd();
 
-  virtual void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
-                                    os::Handler* handler);
+  virtual void RegisterEventHandler(EventCode event_code,
+                                    common::ContextualCallback<void(EventPacketView)> event_handler);
 
   virtual void UnregisterEventHandler(EventCode event_code);
 
-  virtual void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
-                                      os::Handler* handler);
+  virtual void RegisterLeEventHandler(SubeventCode subevent_code,
+                                      common::ContextualCallback<void(LeMetaEventView)> event_handler);
 
   virtual void UnregisterLeEventHandler(SubeventCode subevent_code);
 
-  SecurityInterface* GetSecurityInterface(common::Callback<void(EventPacketView)> event_handler, os::Handler* handler);
+  virtual SecurityInterface* GetSecurityInterface(common::ContextualCallback<void(EventPacketView)> event_handler);
 
-  LeSecurityInterface* GetLeSecurityInterface(common::Callback<void(LeMetaEventView)> event_handler,
-                                              os::Handler* handler);
+  virtual LeSecurityInterface* GetLeSecurityInterface(common::ContextualCallback<void(LeMetaEventView)> event_handler);
 
-  LeAdvertisingInterface* GetLeAdvertisingInterface(common::Callback<void(LeMetaEventView)> event_handler,
-                                                    os::Handler* handler);
+  virtual AclConnectionInterface* GetAclConnectionInterface(
+      common::ContextualCallback<void(EventPacketView)> event_handler,
+      common::ContextualCallback<void(uint16_t, hci::ErrorCode)> on_disconnect);
 
-  LeScanningInterface* GetLeScanningInterface(common::Callback<void(LeMetaEventView)> event_handler,
-                                              os::Handler* handler);
+  virtual LeAclConnectionInterface* GetLeAclConnectionInterface(
+      common::ContextualCallback<void(LeMetaEventView)> event_handler,
+      common::ContextualCallback<void(uint16_t, hci::ErrorCode)> on_disconnect);
+
+  virtual LeAdvertisingInterface* GetLeAdvertisingInterface(
+      common::ContextualCallback<void(LeMetaEventView)> event_handler);
+
+  virtual LeScanningInterface* GetLeScanningInterface(common::ContextualCallback<void(LeMetaEventView)> event_handler);
+
+  std::string ToString() const override {
+    return "Hci Layer";
+  }
+
+  static constexpr std::chrono::milliseconds kHciTimeoutMs = std::chrono::milliseconds(2000);
 
   static const ModuleFactory Factory;
 
+ protected:
+  // Lint.ThenChange(fuzz/fuzz_hci_layer.h)
   void ListDependencies(ModuleList* list) override;
 
   void Start() override;
 
   void Stop() override;
 
-  std::string ToString() const override;
-  static constexpr std::chrono::milliseconds kHciTimeoutMs = std::chrono::milliseconds(2000);
+  virtual void Disconnect(uint16_t handle, ErrorCode reason);
 
  private:
   struct impl;
-  std::unique_ptr<impl> impl_;
+  struct hal_callbacks;
+  impl* impl_;
+  hal_callbacks* hal_callbacks_;
+
+  template <typename T>
+  class CommandInterfaceImpl : public CommandInterface<T> {
+   public:
+    explicit CommandInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+    ~CommandInterfaceImpl() override = default;
+
+    void EnqueueCommand(std::unique_ptr<T> command,
+                        common::ContextualOnceCallback<void(CommandCompleteView)> on_complete) override {
+      hci_.EnqueueCommand(move(command), std::move(on_complete));
+    }
+
+    void EnqueueCommand(std::unique_ptr<T> command,
+                        common::ContextualOnceCallback<void(CommandStatusView)> on_status) override {
+      hci_.EnqueueCommand(move(command), std::move(on_status));
+    }
+    HciLayer& hci_;
+  };
+
+  std::list<common::ContextualCallback<void(uint16_t, ErrorCode)>> disconnect_handlers_;
+  void on_disconnection_complete(EventPacketView event_view);
+
+  // Interfaces
+  CommandInterfaceImpl<ConnectionManagementCommandBuilder> acl_connection_manager_interface_{*this};
+  CommandInterfaceImpl<LeConnectionManagementCommandBuilder> le_acl_connection_manager_interface_{*this};
+  CommandInterfaceImpl<SecurityCommandBuilder> security_interface{*this};
+  CommandInterfaceImpl<LeSecurityCommandBuilder> le_security_interface{*this};
+  CommandInterfaceImpl<LeAdvertisingCommandBuilder> le_advertising_interface{*this};
+  CommandInterfaceImpl<LeScanningCommandBuilder> le_scanning_interface{*this};
 };
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/hci_layer_test.cc b/gd/hci/hci_layer_test.cc
index d796488..7220403 100644
--- a/gd/hci/hci_layer_test.cc
+++ b/gd/hci/hci_layer_test.cc
@@ -148,34 +148,30 @@
 
   void SendHciCommandExpectingStatus(std::unique_ptr<CommandPacketBuilder> command) {
     hci_->EnqueueCommand(std::move(command),
-                         common::Bind(&DependsOnHci::handle_event<CommandStatusView>, common::Unretained(this)),
-                         GetHandler());
+                         GetHandler()->BindOnceOn(this, &DependsOnHci::handle_event<CommandStatusView>));
   }
 
   void SendHciCommandExpectingComplete(std::unique_ptr<CommandPacketBuilder> command) {
     hci_->EnqueueCommand(std::move(command),
-                         common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)),
-                         GetHandler());
+                         GetHandler()->BindOnceOn(this, &DependsOnHci::handle_event<CommandCompleteView>));
   }
 
   void SendSecurityCommandExpectingComplete(std::unique_ptr<SecurityCommandBuilder> command) {
     if (security_interface_ == nullptr) {
-      security_interface_ = hci_->GetSecurityInterface(
-          common::Bind(&DependsOnHci::handle_event<EventPacketView>, common::Unretained(this)), GetHandler());
+      security_interface_ =
+          hci_->GetSecurityInterface(GetHandler()->BindOn(this, &DependsOnHci::handle_event<EventPacketView>));
     }
     hci_->EnqueueCommand(std::move(command),
-                         common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)),
-                         GetHandler());
+                         GetHandler()->BindOnceOn(this, &DependsOnHci::handle_event<CommandCompleteView>));
   }
 
   void SendLeSecurityCommandExpectingComplete(std::unique_ptr<LeSecurityCommandBuilder> command) {
     if (le_security_interface_ == nullptr) {
-      le_security_interface_ = hci_->GetLeSecurityInterface(
-          common::Bind(&DependsOnHci::handle_event<LeMetaEventView>, common::Unretained(this)), GetHandler());
+      le_security_interface_ =
+          hci_->GetLeSecurityInterface(GetHandler()->BindOn(this, &DependsOnHci::handle_event<LeMetaEventView>));
     }
     hci_->EnqueueCommand(std::move(command),
-                         common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)),
-                         GetHandler());
+                         GetHandler()->BindOnceOn(this, &DependsOnHci::handle_event<CommandCompleteView>));
   }
 
   void SendAclData(std::unique_ptr<AclPacketBuilder> acl) {
@@ -215,11 +211,9 @@
   void Start() {
     hci_ = GetDependency<HciLayer>();
     hci_->RegisterEventHandler(EventCode::CONNECTION_COMPLETE,
-                               common::Bind(&DependsOnHci::handle_event<EventPacketView>, common::Unretained(this)),
-                               GetHandler());
+                               GetHandler()->BindOn(this, &DependsOnHci::handle_event<EventPacketView>));
     hci_->RegisterLeEventHandler(SubeventCode::CONNECTION_COMPLETE,
-                                 common::Bind(&DependsOnHci::handle_event<LeMetaEventView>, common::Unretained(this)),
-                                 GetHandler());
+                                 GetHandler()->BindOn(this, &DependsOnHci::handle_event<LeMetaEventView>));
     hci_->GetAclQueueEnd()->RegisterDequeue(GetHandler(),
                                             common::Bind(&DependsOnHci::handle_acl, common::Unretained(this)));
   }
@@ -292,7 +286,7 @@
     fake_registry_.Start<DependsOnHci>(&fake_registry_.GetTestThread());
     hci = static_cast<HciLayer*>(fake_registry_.GetModuleUnderTest(&HciLayer::Factory));
     upper = static_cast<DependsOnHci*>(fake_registry_.GetModuleUnderTest(&DependsOnHci::Factory));
-    ASSERT(fake_registry_.IsStarted<HciLayer>());
+    ASSERT_TRUE(fake_registry_.IsStarted<HciLayer>());
 
     auto reset_sent_status = command_future.wait_for(kTimeout);
     ASSERT_EQ(reset_sent_status, std::future_status::ready);
@@ -345,7 +339,7 @@
   uint16_t conn_interval = 0x0ABC;
   uint16_t conn_latency = 0x0123;
   uint16_t supervision_timeout = 0x0B05;
-  MasterClockAccuracy master_clock_accuracy = MasterClockAccuracy::PPM_50;
+  ClockAccuracy master_clock_accuracy = ClockAccuracy::PPM_50;
   hal->callbacks->hciEventReceived(GetPacketBytes(
       LeConnectionCompleteBuilder::Create(status, handle, role, peer_address_type, peer_address, conn_interval,
                                           conn_latency, supervision_timeout, master_clock_accuracy)));
@@ -355,7 +349,7 @@
   ASSERT_EQ(event_status, std::future_status::ready);
 
   auto event = upper->GetReceivedEvent();
-  ASSERT(LeConnectionCompleteView::Create(LeMetaEventView::Create(EventPacketView::Create(event))).IsValid());
+  ASSERT_TRUE(LeConnectionCompleteView::Create(LeMetaEventView::Create(EventPacketView::Create(event))).IsValid());
 }
 
 TEST_F(HciTest, noOpCredits) {
@@ -398,8 +392,9 @@
   ASSERT_EQ(event_status, std::future_status::ready);
 
   auto event = upper->GetReceivedEvent();
-  ASSERT(ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
-             .IsValid());
+  ASSERT_TRUE(
+      ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
+          .IsValid());
 }
 
 TEST_F(HciTest, creditsTest) {
@@ -445,8 +440,9 @@
   ASSERT_EQ(event_status, std::future_status::ready);
 
   auto event = upper->GetReceivedEvent();
-  ASSERT(ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
-             .IsValid());
+  ASSERT_TRUE(
+      ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
+          .IsValid());
 
   // Verify that the second one is sent
   command_sent_status = command_future.wait_for(kTimeout);
@@ -474,8 +470,9 @@
   ASSERT_EQ(event_status, std::future_status::ready);
 
   event = upper->GetReceivedEvent();
-  ASSERT(ReadLocalSupportedCommandsCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
-             .IsValid());
+  ASSERT_TRUE(
+      ReadLocalSupportedCommandsCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
+          .IsValid());
   // Verify that the third one is sent
   command_sent_status = command_future.wait_for(kTimeout);
   ASSERT_EQ(command_sent_status, std::future_status::ready);
@@ -498,8 +495,9 @@
   event_status = event_future.wait_for(kTimeout);
   ASSERT_EQ(event_status, std::future_status::ready);
   event = upper->GetReceivedEvent();
-  ASSERT(ReadLocalSupportedFeaturesCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
-             .IsValid());
+  ASSERT_TRUE(
+      ReadLocalSupportedFeaturesCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
+          .IsValid());
 }
 
 TEST_F(HciTest, leSecurityInterfaceTest) {
diff --git a/gd/hci/hci_packets.pdl b/gd/hci/hci_packets.pdl
index 5a24468..47237e2 100644
--- a/gd/hci/hci_packets.pdl
+++ b/gd/hci/hci_packets.pdl
@@ -203,6 +203,8 @@
   READ_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C79,
   WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C7A,
   READ_LOCAL_OOB_EXTENDED_DATA = 0x0C7D,
+  SET_ECOSYSTEM_BASE_INTERVAL = 0x0C82,
+  CONFIGURE_DATA_PATH = 0x0C83,
 
   // INFORMATIONAL_PARAMETERS
   READ_LOCAL_VERSION_INFORMATION = 0x1001,
@@ -212,7 +214,10 @@
   READ_BUFFER_SIZE = 0x1005,
   READ_BD_ADDR = 0x1009,
   READ_DATA_BLOCK_SIZE = 0x100A,
-  READ_LOCAL_SUPPORTED_CODECS = 0x100B,
+  READ_LOCAL_SUPPORTED_CODECS_V1 = 0x100B,
+  READ_LOCAL_SUPPORTED_CODECS_V2 = 0x100D,
+  READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES = 0x100E,
+  READ_LOCAL_SUPPORTED_CONTROLLER_DELAY = 0X100F,
 
   // STATUS_PARAMETERS
   READ_FAILED_CONTACT_COUNTER = 0x1401,
@@ -232,7 +237,7 @@
 
   // LE_CONTROLLER
   LE_SET_EVENT_MASK = 0x2001,
-  LE_READ_BUFFER_SIZE = 0x2002,
+  LE_READ_BUFFER_SIZE_V1 = 0x2002,
   LE_READ_LOCAL_SUPPORTED_FEATURES = 0x2003,
   LE_SET_RANDOM_ADDRESS = 0x2005,
   LE_SET_ADVERTISING_PARAMETERS = 0x2006,
@@ -244,10 +249,10 @@
   LE_SET_SCAN_ENABLE = 0x200C,
   LE_CREATE_CONNECTION = 0x200D,
   LE_CREATE_CONNECTION_CANCEL = 0x200E,
-  LE_READ_WHITE_LIST_SIZE = 0x200F,
-  LE_CLEAR_WHITE_LIST = 0x2010,
-  LE_ADD_DEVICE_TO_WHITE_LIST = 0x2011,
-  LE_REMOVE_DEVICE_FROM_WHITE_LIST = 0x2012,
+  LE_READ_CONNECT_LIST_SIZE = 0x200F,
+  LE_CLEAR_CONNECT_LIST = 0x2010,
+  LE_ADD_DEVICE_TO_CONNECT_LIST = 0x2011,
+  LE_REMOVE_DEVICE_FROM_CONNECT_LIST = 0x2012,
   LE_CONNECTION_UPDATE = 0x2013,
   LE_SET_HOST_CHANNEL_CLASSIFICATION = 0x2014,
   LE_READ_CHANNEL_MAP = 0x2015,
@@ -309,7 +314,34 @@
   LE_READ_RF_PATH_COMPENSATION_POWER = 0x204C,
   LE_WRITE_RF_PATH_COMPENSATION_POWER = 0x204D,
   LE_SET_PRIVACY_MODE = 0x204E,
+  LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE = 0X2059,
+  LE_PERIODIC_ADVERTISING_SYNC_TRANSFER = 0X205A,
+  LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER = 0X205B,
+  LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 0X205C,
+  LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 0X205D,
   LE_GENERATE_DHKEY_COMMAND = 0x205E,
+  LE_MODIFY_SLEEP_CLOCK_ACCURACY = 0X205F,
+  LE_READ_BUFFER_SIZE_V2 = 0x2060,
+  LE_READ_ISO_TX_SYNC = 0X2061,
+  LE_SET_CIG_PARAMETERS = 0X2062,
+  LE_CREATE_CIS = 0X2064,
+  LE_REMOVE_CIG = 0X2065,
+  LE_ACCEPT_CIS_REQUEST = 0X2066,
+  LE_REJECT_CIS_REQUEST = 0X2067,
+  LE_CREATE_BIG = 0X2068,
+  LE_TERMINATE_BIG = 0X206A,
+  LE_BIG_CREATE_SYNC = 0X206B,
+  LE_BIG_TERMINATE_SYNC = 0X206C,
+  LE_REQUEST_PEER_SCA = 0X206D,
+  LE_SETUP_ISO_DATA_PATH = 0X206E,
+  LE_REMOVE_ISO_DATA_PATH = 0X206F,
+  LE_SET_HOST_FEATURE = 0X2074,
+  LE_READ_ISO_LINK_QUALITY = 0X2075,
+  LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL = 0X2076,
+  LE_READ_REMOTE_TRANSMIT_POWER_LEVEL = 0X2077,
+  LE_SET_PATH_LOSS_REPORTING_PARAMETERS = 0X2078,
+  LE_SET_PATH_LOSS_REPORTING_ENABLE = 0X2079,
+  LE_SET_TRANSMIT_POWER_REPORTING_ENABLE = 0X207A,
 
   // VENDOR_SPECIFIC
   LE_GET_VENDOR_CAPABILITIES = 0xFD53,
@@ -457,7 +489,7 @@
   READ_LE_HOST_SUPPORT = 245,
   WRITE_LE_HOST_SUPPORT = 246,
   LE_SET_EVENT_MASK = 250,
-  LE_READ_BUFFER_SIZE = 251,
+  LE_READ_BUFFER_SIZE_V1 = 251,
   LE_READ_LOCAL_SUPPORTED_FEATURES = 252,
   LE_SET_RANDOM_ADDRESS = 254,
   LE_SET_ADVERTISING_PARAMETERS = 255,
@@ -469,10 +501,10 @@
   LE_SET_SCAN_ENABLE = 263,
   LE_CREATE_CONNECTION = 264,
   LE_CREATE_CONNECTION_CANCEL = 265,
-  LE_READ_WHITE_LIST_SIZE = 266,
-  LE_CLEAR_WHITE_LIST = 267,
-  LE_ADD_DEVICE_TO_WHITE_LIST = 270,
-  LE_REMOVE_DEVICE_FROM_WHITE_LIST = 271,
+  LE_READ_CONNECT_LIST_SIZE = 266,
+  LE_CLEAR_CONNECT_LIST = 267,
+  LE_ADD_DEVICE_TO_CONNECT_LIST = 270,
+  LE_REMOVE_DEVICE_FROM_CONNECT_LIST = 271,
   LE_CONNECTION_UPDATE = 272,
   LE_SET_HOST_CHANNEL_CLASSIFICATION = 273,
   LE_READ_CHANNEL_MAP = 274,
@@ -488,7 +520,7 @@
   LE_TEST_END = 286,
   ENHANCED_SETUP_SYNCHRONOUS_CONNECTION = 293,
   ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION = 294,
-  READ_LOCAL_SUPPORTED_CODECS = 295,
+  READ_LOCAL_SUPPORTED_CODECS_V1 = 295,
   READ_SECURE_CONNECTIONS_HOST_SUPPORT = 322,
   WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = 323,
   READ_LOCAL_OOB_EXTENDED_DATA = 326,
@@ -540,7 +572,40 @@
   LE_READ_RF_PATH_COMPENSATION_POWER = 390,
   LE_WRITE_RF_PATH_COMPENSATION_POWER = 391,
   LE_SET_PRIVACY_MODE = 392,
+
+  LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE = 405,
+  LE_PERIODIC_ADVERTISING_SYNC_TRANSFER = 406,
+  LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER = 407,
+  LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 410,
+  LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 411,
   LE_GENERATE_DHKEY_COMMAND = 412,
+  LE_MODIFY_SLEEP_CLOCK_ACCURACY = 414,
+  LE_READ_BUFFER_SIZE_V2 = 415,
+  LE_READ_ISO_TX_SYNC = 416,
+  LE_SET_CIG_PARAMETERS = 417,
+  LE_CREATE_CIS = 421,
+  LE_REMOVE_CIG = 422,
+  LE_ACCEPT_CIS_REQUEST = 423,
+  LE_REJECT_CIS_REQUEST = 424,
+  LE_CREATE_BIG = 425,
+  LE_TERMINATE_BIG = 427,
+  LE_BIG_CREATE_SYNC = 430,
+  LE_BIG_TERMINATE_SYNC = 431,
+  LE_REQUEST_PEER_SCA = 432,
+  LE_SETUP_ISO_DATA_PATH = 433,
+  LE_REMOVE_ISO_DATA_PATH = 434,
+  LE_SET_HOST_FEATURE = 441,
+  LE_READ_ISO_LINK_QUALITY = 442,
+  LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL = 443,
+  LE_READ_REMOTE_TRANSMIT_POWER_LEVEL = 444,
+  LE_SET_PATH_LOSS_REPORTING_PARAMETERS = 445,
+  LE_SET_PATH_LOSS_REPORTING_ENABLE = 446,
+  LE_SET_TRANSMIT_POWER_REPORTING_ENABLE = 447,
+  SET_ECOSYSTEM_BASE_INTERVAL = 451,
+  READ_LOCAL_SUPPORTED_CODECS_V2 = 452,
+  READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES = 453,
+  READ_LOCAL_SUPPORTED_CONTROLLER_DELAY = 454,
+  CONFIGURE_DATA_PATH = 455,
 }
 
 packet CommandPacket {
@@ -647,6 +712,21 @@
   SCAN_TIMEOUT = 0x11,
   ADVERTISING_SET_TERMINATED = 0x12,
   SCAN_REQUEST_RECEIVED = 0x13,
+  CHANNEL_SELECTION_ALGORITHM = 0X14,
+  CONNECTIONLESS_IQ_REPORT = 0X15,
+  CONNECTION_IQ_REPORT = 0X16,
+  CTE_REQUEST_FAILED = 0X17,
+  PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED = 0X18,
+  CIS_ESTABLISHED = 0X19,
+  CIS_REQUEST = 0X1A,
+  CREATE_BIG_COMPLETE = 0X1B,
+  TERMINATE_BIG_COMPLETE = 0X1C,
+  BIG_SYNC_ESTABLISHED = 0X1D,
+  BIG_SYNC_LOST = 0X1E,
+  REQUEST_PEER_SCA_COMPLETE = 0X1F,
+  PATH_LOSS_THRESHOLD = 0X20,
+  TRANSMIT_POWER_REPORTING = 0X21,
+  BIG_INFO_ADVERTISING_REPORT = 0X22,
 }
 
 // Vendor specific events
@@ -699,6 +779,7 @@
   UNSPECIFIED_ERROR = 0x1F,
   UNSUPPORTED_LMP_OR_LL_PARAMETER = 0x20,
   ROLE_CHANGE_NOT_ALLOWED = 0x21,
+  LINK_LAYER_COLLISION = 0x23,
   ENCRYPTION_MODE_NOT_ACCEPTABLE = 0x25,
   CONTROLLER_BUSY = 0x3A,
 }
@@ -953,7 +1034,7 @@
   bd_addr : Address,
 }
 
-packet ReadRemoteSupportedFeatures : DiscoveryCommand (op_code = READ_REMOTE_SUPPORTED_FEATURES) {
+packet ReadRemoteSupportedFeatures : ConnectionManagementCommand (op_code = READ_REMOTE_SUPPORTED_FEATURES) {
   connection_handle : 12,
   _reserved_ : 4,
 }
@@ -961,7 +1042,7 @@
 packet ReadRemoteSupportedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_SUPPORTED_FEATURES) {
 }
 
-packet ReadRemoteExtendedFeatures : DiscoveryCommand (op_code = READ_REMOTE_EXTENDED_FEATURES) {
+packet ReadRemoteExtendedFeatures : ConnectionManagementCommand (op_code = READ_REMOTE_EXTENDED_FEATURES) {
   connection_handle : 12,
   _reserved_ : 4,
   page_number : 8,
@@ -970,7 +1051,7 @@
 packet ReadRemoteExtendedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_EXTENDED_FEATURES) {
 }
 
-packet ReadRemoteVersionInformation : DiscoveryCommand (op_code = READ_REMOTE_VERSION_INFORMATION) {
+packet ReadRemoteVersionInformation : ConnectionManagementCommand (op_code = READ_REMOTE_VERSION_INFORMATION) {
   connection_handle : 12,
   _reserved_ : 4,
 }
@@ -1714,7 +1795,7 @@
 }
 
 packet WriteLinkSupervisionTimeout : ConnectionManagementCommand (op_code = WRITE_LINK_SUPERVISION_TIMEOUT) {
-  handle : 12,
+  connection_handle : 12,
   _reserved_ : 4,
   link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
 }
@@ -1971,6 +2052,30 @@
   r_256 : 8[16],
 }
 
+packet SetEcosystemBaseInterval : CommandPacket (op_code = SET_ECOSYSTEM_BASE_INTERVAL) {
+  interval : 16,
+}
+
+packet SetEcosystemBaseIntervalComplete : CommandComplete (command_op_code = SET_ECOSYSTEM_BASE_INTERVAL) {
+  status : ErrorCode,
+}
+
+enum DataPathDirection : 8 {
+  INPUT = 0,
+  OUTPUT = 1,
+}
+
+packet ConfigureDataPath : CommandPacket (op_code = CONFIGURE_DATA_PATH) {
+  data_path_direction : DataPathDirection,
+  data_path_id : 8,
+  _size_(vendor_specific_config) : 8,
+  vendor_specific_config : 8[],
+}
+
+packet ConfigureDataPathComplete : CommandComplete (command_op_code = CONFIGURE_DATA_PATH) {
+  status : ErrorCode,
+}
+
 
   // INFORMATIONAL_PARAMETERS
 packet ReadLocalVersionInformation : CommandPacket (op_code = READ_LOCAL_VERSION_INFORMATION) {
@@ -2066,10 +2171,10 @@
 packet ReadDataBlockSize : CommandPacket (op_code = READ_DATA_BLOCK_SIZE) {
 }
 
-packet ReadLocalSupportedCodecs : CommandPacket (op_code = READ_LOCAL_SUPPORTED_CODECS) {
+packet ReadLocalSupportedCodecsV1 : CommandPacket (op_code = READ_LOCAL_SUPPORTED_CODECS_V1) {
 }
 
-packet ReadLocalSupportedCodecsComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODECS) {
+packet ReadLocalSupportedCodecsV1Complete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODECS_V1) {
   status : ErrorCode,
   _size_(supported_codecs) : 8,
   supported_codecs : 8[],
@@ -2077,6 +2182,72 @@
   vendor_specific_codecs : 32[],
 }
 
+packet ReadLocalSupportedCodecsV2 : CommandPacket (op_code = READ_LOCAL_SUPPORTED_CODECS_V2) {
+}
+
+group CodecTransport {
+  br_edr : 1,
+  br_edr_sco_and_esco : 1,
+  le_cis : 1,
+  le_bis : 1,
+  _reserved_ : 4,
+}
+
+struct CodecConfiguration {
+  codec_id : 8,
+  CodecTransport,
+}
+
+struct VendorCodecConfiguration {
+  company_id : 16,
+  codec_vendor_id : 16,
+  CodecTransport,
+}
+
+packet ReadLocalSupportedCodecsV2Complete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODECS_V2) {
+  status : ErrorCode,
+  _size_(supported_codecs) : 8,
+  supported_codecs : CodecConfiguration[],
+  _size_(vendor_specific_codecs) : 8,
+  vendor_specific_codecs : VendorCodecConfiguration[],
+}
+
+packet ReadLocalSupportedCodecCapabilities : CommandPacket (op_code = READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES) {
+  codec_id : 8,
+  company_id : 16,
+  codec_vendor_id : 16,
+  CodecTransport,
+  direction : DataPathDirection,
+}
+
+struct CodecCapability {
+  _size_(capability) : 8,
+  capability : 8[],
+}
+
+packet ReadLocalSupportedCodecCapabilitiesComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES) {
+  status : ErrorCode,
+  _count_(codec_capabilities) : 8,
+  codec_capabilities : CodecCapability[],
+}
+
+packet ReadLocalSupportedControllerDelay : CommandPacket (op_code = READ_LOCAL_SUPPORTED_CONTROLLER_DELAY) {
+  codec_id : 8,
+  company_id : 16,
+  codec_vendor_id : 16,
+  CodecTransport,
+  direction : DataPathDirection,
+  _size_(codec_configuration) : 8,
+  codec_configuration : 8[],
+}
+
+packet ReadLocalSupportedControllerDelayComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CONTROLLER_DELAY) {
+  status : ErrorCode,
+  min_controller_delay : 24,
+  max_controller_delay : 24,
+}
+
+
   // STATUS_PARAMETERS
 packet ReadFailedContactCounter : ConnectionManagementCommand (op_code = READ_FAILED_CONTACT_COUNTER) {
   connection_handle : 12,
@@ -2206,7 +2377,7 @@
   status : ErrorCode,
 }
 
-packet WriteSimplePairingDebugMode : CommandPacket (op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) {
+packet WriteSimplePairingDebugMode : SecurityCommand (op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) {
   simple_pairing_debug_mode : Enable,
 }
 
@@ -2234,7 +2405,7 @@
   status : ErrorCode,
 }
 
-packet LeReadBufferSize : CommandPacket (op_code = LE_READ_BUFFER_SIZE) {
+packet LeReadBufferSizeV1 : CommandPacket (op_code = LE_READ_BUFFER_SIZE_V1) {
 }
 
 struct LeBufferSize {
@@ -2242,7 +2413,7 @@
   total_num_le_packets : 8,
 }
 
-packet LeReadBufferSizeComplete : CommandComplete (command_op_code = LE_READ_BUFFER_SIZE) {
+packet LeReadBufferSizeV1Complete : CommandComplete (command_op_code = LE_READ_BUFFER_SIZE_V1) {
   status : ErrorCode,
   le_buffer_size : LeBufferSize,
 }
@@ -2265,9 +2436,9 @@
 
 enum AdvertisingFilterPolicy : 2 {
   ALL_DEVICES = 0, // Default
-  WHITELISTED_SCAN = 1,
-  WHITELISTED_CONNECT = 2,
-  WHITELISTED_SCAN_AND_CONNECT = 3,
+  LISTED_SCAN = 1,
+  LISTED_CONNECT = 2,
+  LISTED_SCAN_AND_CONNECT = 3,
 }
 
 enum PeerAddressType : 8 {
@@ -2275,7 +2446,7 @@
   RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x01,
 }
 
-enum AdvertisingEventType : 8 {
+enum AdvertisingType : 8 {
   ADV_IND = 0x00,
   ADV_DIRECT_IND = 0x01,
   ADV_SCAN_IND = 0x02,
@@ -2293,7 +2464,7 @@
 packet LeSetAdvertisingParameters : LeAdvertisingCommand (op_code = LE_SET_ADVERTISING_PARAMETERS) {
   interval_min : 16,
   interval_max : 16,
-  type : AdvertisingEventType,
+  type : AdvertisingType,
   own_address_type : AddressType,
   peer_address_type : PeerAddressType,
   peer_address : Address,
@@ -2347,11 +2518,11 @@
   ACTIVE = 0x01,
 }
 
-enum LeSetScanningFilterPolicy : 8 {
+enum LeScanningFilterPolicy : 8 {
   ACCEPT_ALL = 0x00, // Default
-  WHITE_LIST_ONLY = 0x01,
+  CONNECT_LIST_ONLY = 0x01,
   CHECK_INITIATORS_IDENTITY = 0x02,
-  WHITE_LIST_AND_INITIATORS_IDENTITY = 0x03,
+  CONNECT_LIST_AND_INITIATORS_IDENTITY = 0x03,
 }
 
 packet LeSetScanParameters : LeScanningCommand (op_code = LE_SET_SCAN_PARAMETERS) {
@@ -2359,7 +2530,7 @@
   le_scan_interval : 16, // 0x0004-0x4000 Default 0x10 (10ms)
   le_scan_window : 16, // Default 0x10 (10ms)
   own_address_type : AddressType,
-  scanning_filter_policy : LeSetScanningFilterPolicy,
+  scanning_filter_policy : LeScanningFilterPolicy,
 }
 
 packet LeSetScanParametersComplete : CommandComplete (command_op_code = LE_SET_SCAN_PARAMETERS) {
@@ -2377,7 +2548,7 @@
 
 enum InitiatorFilterPolicy : 8 {
   USE_PEER_ADDRESS = 0x00,
-  USE_WHITE_LIST = 0x01,
+  USE_CONNECT_LIST = 0x01,
 }
 
 enum OwnAddressType : 8 {
@@ -2408,49 +2579,46 @@
 packet LeCreateConnectionCancel : LeConnectionManagementCommand (op_code = LE_CREATE_CONNECTION_CANCEL) {
 }
 
-packet LeCreateConnectionCancelStatus : CommandStatus (command_op_code = LE_CREATE_CONNECTION_CANCEL) {
-}
-
 packet LeCreateConnectionCancelComplete : CommandComplete (command_op_code = LE_CREATE_CONNECTION_CANCEL) {
   status : ErrorCode,
 }
 
-packet LeReadWhiteListSize : LeConnectionManagementCommand (op_code = LE_READ_WHITE_LIST_SIZE) {
+packet LeReadConnectListSize : CommandPacket (op_code = LE_READ_CONNECT_LIST_SIZE) {
 }
 
-packet LeReadWhiteListSizeComplete : CommandComplete (command_op_code = LE_READ_WHITE_LIST_SIZE) {
+packet LeReadConnectListSizeComplete : CommandComplete (command_op_code = LE_READ_CONNECT_LIST_SIZE) {
   status : ErrorCode,
-  white_list_size : 8,
+  connect_list_size : 8,
 }
 
-packet LeClearWhiteList : LeConnectionManagementCommand (op_code = LE_CLEAR_WHITE_LIST) {
+packet LeClearConnectList : LeConnectionManagementCommand (op_code = LE_CLEAR_CONNECT_LIST) {
 }
 
-packet LeClearWhiteListComplete : CommandComplete (command_op_code = LE_CLEAR_WHITE_LIST) {
+packet LeClearConnectListComplete : CommandComplete (command_op_code = LE_CLEAR_CONNECT_LIST) {
   status : ErrorCode,
 }
 
-enum WhiteListAddressType : 8 {
+enum ConnectListAddressType : 8 {
   PUBLIC = 0x00,
   RANDOM = 0x01,
   ANONYMOUS_ADVERTISERS = 0xFF,
 }
 
-packet LeAddDeviceToWhiteList : LeConnectionManagementCommand (op_code = LE_ADD_DEVICE_TO_WHITE_LIST) {
-  address_type : WhiteListAddressType,
+packet LeAddDeviceToConnectList : LeConnectionManagementCommand (op_code = LE_ADD_DEVICE_TO_CONNECT_LIST) {
+  address_type : ConnectListAddressType,
   address : Address,
 }
 
-packet LeAddDeviceToWhiteListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_WHITE_LIST) {
+packet LeAddDeviceToConnectListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_CONNECT_LIST) {
   status : ErrorCode,
 }
 
-packet LeRemoveDeviceFromWhiteList : LeConnectionManagementCommand (op_code = LE_REMOVE_DEVICE_FROM_WHITE_LIST) {
-  address_type : WhiteListAddressType,
+packet LeRemoveDeviceFromConnectList : LeConnectionManagementCommand (op_code = LE_REMOVE_DEVICE_FROM_CONNECT_LIST) {
+  address_type : ConnectListAddressType,
   address : Address,
 }
 
-packet LeRemoveDeviceFromWhiteListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_WHITE_LIST) {
+packet LeRemoveDeviceFromConnectListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_CONNECT_LIST) {
   status : ErrorCode,
 }
 
@@ -2658,11 +2826,12 @@
   status : ErrorCode,
 }
 
-packet LeReadResolvingListSize : LeSecurityCommand (op_code = LE_READ_RESOLVING_LIST_SIZE) {
+packet LeReadResolvingListSize : CommandPacket (op_code = LE_READ_RESOLVING_LIST_SIZE) {
 }
 
 packet LeReadResolvingListSizeComplete : CommandComplete (command_op_code = LE_READ_RESOLVING_LIST_SIZE) {
   status : ErrorCode,
+  resolving_list_size : 8,
 }
 
 packet LeReadPeerResolvableAddress : LeSecurityCommand (op_code = LE_READ_PEER_RESOLVABLE_ADDRESS) {
@@ -2781,9 +2950,9 @@
   advertising_filter_policy : AdvertisingFilterPolicy,
   _reserved_ : 6,
   advertising_tx_power : 8, // -127 to +20, 0x7F - no preference
-  primary_advertising_phy : PrimaryPhyType,
+  _fixed_ = 0x1 : 8, // PrimaryPhyType LE_1M
   _reserved_ : 8, // secondary_advertising_max_skip
-  _reserved_ : 8, // secondary_advertising_phy
+  _fixed_ = 0x1 : 8, // secondary_advertising_phy LE_1M
   advertising_sid : 8, // SID subfield from the ADI field of the PDU
   scan_request_notification_enable : Enable,
 }
@@ -2820,7 +2989,7 @@
   INTERMEDIATE_FRAGMENT = 0,
   FIRST_FRAGMENT = 1,
   LAST_FRAGMENT = 2,
-  COMPLETE_ADVERTISMENT = 3,
+  COMPLETE_ADVERTISEMENT = 3,
   UNCHANGED_DATA = 4,
 }
 
@@ -2983,7 +3152,7 @@
 
 packet LeSetExtendedScanParameters : LeScanningCommand (op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) {
   own_address_type : AddressType,
-  scanning_filter_policy : LeSetScanningFilterPolicy,
+  scanning_filter_policy : LeScanningFilterPolicy,
   scanning_phys : 8,
   parameters : PhyScanParameters[],
 }
@@ -3009,8 +3178,27 @@
   status : ErrorCode,
 }
 
+struct LeCreateConnPhyScanParameters {
+  scan_interval : 16, // 0x0004-0xFFFF
+  scan_window : 16, // < = LeScanInterval
+  conn_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_latency : 16, // 0x0006-0x01F3
+  supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+  min_ce_length : 16, // 0.625ms
+  max_ce_length : 16, // 0.625ms
+}
+
 packet LeExtendedCreateConnection : LeConnectionManagementCommand (op_code = LE_EXTENDED_CREATE_CONNECTION) {
-  _payload_,  // placeholder (unimplemented)
+  initiator_filter_policy : InitiatorFilterPolicy,
+  own_address_type : OwnAddressType,
+  peer_address_type : AddressType,
+  peer_address : Address,
+  initiating_phys : 8,
+  phy_scan_parameters : LeCreateConnPhyScanParameters[],
+}
+
+packet LeExtendedCreateConnectionStatus : CommandStatus (command_op_code = LE_EXTENDED_CREATE_CONNECTION) {
 }
 
 packet LePeriodicAdvertisingCreateSync : LeAdvertisingCommand (op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC) {
@@ -3068,6 +3256,71 @@
   status : ErrorCode,
 }
 
+packet LeSetPeriodicAdvertisingReceiveEnable : CommandPacket (op_code = LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE) {
+  sync_handle : 12,
+  _reserved_ : 4,
+  enable : 8,
+}
+
+packet LeSetPeriodicAdvertisingReceiveEnableComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE) {
+  status : ErrorCode,
+}
+
+packet LePeriodicAdvertisingSyncTransfer : CommandPacket (op_code = LE_PERIODIC_ADVERTISING_SYNC_TRANSFER) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  service_data : 16,
+  sync_handle: 12,
+  _reserved_ : 4,
+}
+
+packet LePeriodicAdvertisingSyncTransferComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_SYNC_TRANSFER) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LePeriodicAdvertisingSetInfoTransfer : CommandPacket (op_code = LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  service_data : 16,
+  advertising_handle: 8,
+}
+
+packet LePeriodicAdvertisingSetInfoTransferComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+enum SyncTransferMode : 8 {
+  NO_SYNC = 0,
+  SEND_SYNC_RECEIVED_DISABLE_REPORTS = 1,
+  SEND_SYNC_RECEIVED_SEND_REPORTS = 2,
+}
+
+packet LeSetPeriodicAdvertisingSyncTransferParameters : CommandPacket (op_code = LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  mode : SyncTransferMode,
+  skip: 16,
+  sync_timeout : 16,
+  cte_type : 8,
+}
+
+packet LeSetPeriodicAdvertisingSyncTransferParametersComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeSetDefaultPeriodicAdvertisingSyncTransferParameters : CommandPacket (op_code = LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) {
+  mode : SyncTransferMode,
+  skip: 16,
+  sync_timeout : 16,
+  cte_type : 8,
+}
+
 enum UseDebugKey : 8 {
   USE_GENERATED_KEY = 0,
   USE_DEBUG_KEY = 1,
@@ -3081,6 +3334,323 @@
 packet LeGenerateDhkeyCommandStatus : CommandStatus (command_op_code = LE_GENERATE_DHKEY_COMMAND) {
 }
 
+enum ScaAction : 8 {
+  MORE_ACCURATE_CLOCK = 0,
+  LESS_ACCURATE_CLOCK = 1,
+}
+
+packet LeModifySleepClockAccuracy : CommandPacket (op_code = LE_MODIFY_SLEEP_CLOCK_ACCURACY) {
+  action : ScaAction,
+}
+
+packet LeModifySleepClockAccuracyComplete : CommandComplete (command_op_code = LE_MODIFY_SLEEP_CLOCK_ACCURACY) {
+  status : ErrorCode,
+}
+
+packet LeReadBufferSizeV2 : CommandPacket (op_code = LE_READ_BUFFER_SIZE_V2) {
+}
+
+packet LeReadBufferSizeV2Complete : CommandComplete (command_op_code = LE_READ_BUFFER_SIZE_V2) {
+  status : ErrorCode,
+  le_buffer_size : LeBufferSize,
+  iso_buffer_size : LeBufferSize,
+}
+
+packet LeReadIsoTxSync : CommandPacket (op_code = LE_READ_ISO_TX_SYNC) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeReadIsoTxSyncComplete : CommandComplete (command_op_code = LE_READ_ISO_TX_SYNC) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  packet_sequence_number : 16,
+  timestamp : 32,
+  time_offset : 24,
+}
+
+struct CisParametersConfig {
+  cis_id : 8,
+  max_sdu_m_to_s : 12,
+  _reserved_ : 4,
+  max_sdu_s_to_m : 12,
+  _reserved_ : 4,
+  phy_m_to_s : SecondaryPhyType,
+  phy_s_to_m : SecondaryPhyType,
+  rtn_m_to_s : 4,
+  _reserved_ : 4,
+  rtn_s_to_m : 4,
+  _reserved_ : 4,
+}
+
+enum Packing : 8 {
+  SEQUENTIAL = 0,
+  INTERLEAVED = 1,
+}
+
+enum ClockAccuracy : 8 {
+  PPM_500 = 0x00,
+  PPM_250 = 0x01,
+  PPM_150 = 0x02,
+  PPM_100 = 0x03,
+  PPM_75 = 0x04,
+  PPM_50 = 0x05,
+  PPM_30 = 0x06,
+  PPM_20 = 0x07,
+}
+
+packet LeSetCigParameters : CommandPacket (op_code = LE_SET_CIG_PARAMETERS) {
+  cig_id : 8,
+  sdu_interval_m_to_s : 24,
+  sdu_interval_s_to_m : 24,
+  slaves_clock_accuracy : ClockAccuracy,
+  packing : Packing,
+  framing : Enable,
+  max_transport_latency_m_to_s : 16,
+  max_transport_latency_s_to_m : 16,
+  _count_(cis_config) : 8,
+  cis_config : CisParametersConfig[],
+}
+
+packet LeSetCigParametersComplete : CommandComplete (command_op_code = LE_SET_CIG_PARAMETERS) {
+  status : ErrorCode,
+  cig_id : 8,
+  _count_(connection_handle) : 8,
+  connection_handle : 16[],
+}
+
+struct CreateCisConfig {
+  cis_connection_handle : 12,
+  _reserved_ : 4,
+  acl_connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeCreateCis : CommandPacket (op_code = LE_CREATE_CIS) {
+  _count_(cis_config) : 8,
+  cis_config : CreateCisConfig[],
+}
+
+packet LeCreateCisStatus : CommandStatus (command_op_code = LE_CREATE_CIS) {
+}
+
+packet LeRemoveCig : CommandPacket (op_code = LE_REMOVE_CIG) {
+  cig_id : 8,
+}
+
+packet LeRemoveCigComplete : CommandComplete (command_op_code = LE_REMOVE_CIG) {
+  status : ErrorCode,
+  cig_id : 8,
+}
+
+packet LeAcceptCisRequest : CommandPacket (op_code = LE_ACCEPT_CIS_REQUEST) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeAcceptCisRequestStatus : CommandStatus (command_op_code = LE_ACCEPT_CIS_REQUEST) {
+}
+
+packet LeRejectCisRequest : CommandPacket (op_code = LE_REJECT_CIS_REQUEST) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  reason : ErrorCode,
+}
+
+packet LeRejectCisRequestComplete : CommandComplete (command_op_code = LE_REJECT_CIS_REQUEST) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeCreateBig : CommandPacket (op_code = LE_CREATE_BIG) {
+  big_handle : 8,
+  advertising_handle : 8,
+  num_bis : 8,
+  sdu_interval : 24,
+  max_sdu : 16,
+  max_transport_latency : 16,
+  rtn : 4,
+  _reserved_ : 4,
+  phy : SecondaryPhyType,
+  packing : Packing,
+  framing : Enable,
+  encryption : Enable,
+  broadcast_code: 16[],
+}
+
+packet LeCreateBigStatus : CommandStatus (command_op_code = LE_CREATE_BIG) {
+}
+
+packet LeTerminateBig : CommandPacket (op_code = LE_TERMINATE_BIG) {
+  big_handle : 8,
+  reason : ErrorCode,
+}
+
+packet LeTerminateBigStatus : CommandStatus (command_op_code = LE_TERMINATE_BIG) {
+}
+
+packet LeBigCreateSync : CommandPacket (op_code = LE_BIG_CREATE_SYNC) {
+  big_handle : 8,
+  sync_handle : 12,
+  _reserved_ : 4,
+  encryption : Enable,
+  broadcast_code : 16[],
+  mse : 5,
+  _reserved_ : 3,
+  big_sync_timeout : 16,
+  _count_(bis) : 8,
+  bis : 8[],
+}
+
+packet LeBigCreateSyncStatus : CommandStatus (command_op_code = LE_BIG_CREATE_SYNC) {
+}
+
+packet LeBigTerminateSync : CommandPacket (op_code = LE_BIG_TERMINATE_SYNC) {
+  big_handle : 8,
+}
+
+packet LeBigTerminateSyncComplete : CommandComplete (command_op_code = LE_BIG_TERMINATE_SYNC) {
+  status : ErrorCode,
+  big_handle : 8,
+}
+
+packet LeRequestPeerSca : CommandPacket (op_code = LE_REQUEST_PEER_SCA) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeRequestPeerScaStatus : CommandStatus (command_op_code = LE_REQUEST_PEER_SCA) {
+}
+
+packet LeSetupIsoDataPath : CommandPacket (op_code = LE_SETUP_ISO_DATA_PATH) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  data_path_direction : DataPathDirection,
+  data_path_id : 8,
+  codec_id : 40,
+  controller_delay : 24,
+  _count_(codec_configuration) : 8,
+  codec_configuration : 8[],
+}
+
+packet LeSetupIsoDataPathComplete : CommandComplete (command_op_code = LE_SETUP_ISO_DATA_PATH) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeRemoveIsoDataPath : CommandPacket (op_code = LE_REMOVE_ISO_DATA_PATH) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  data_path_direction : DataPathDirection,
+}
+
+packet LeRemoveIsoDataPathComplete : CommandComplete (command_op_code = LE_REMOVE_ISO_DATA_PATH) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeSetHostFeature : CommandPacket (op_code = LE_SET_HOST_FEATURE) {
+  bit_number : 8,
+  bit_value:  Enable,
+}
+
+packet LeSetHostFeatureComplete : CommandComplete (command_op_code = LE_SET_HOST_FEATURE) {
+  status : ErrorCode,
+}
+
+packet LeReadIsoLinkQuality : CommandPacket (op_code = LE_READ_ISO_LINK_QUALITY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeReadIsoLinkQualityComplete : CommandComplete (command_op_code = LE_READ_ISO_LINK_QUALITY) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  tx_unacked_packets : 32,
+  tx_flushed_packets : 32,
+  tx_last_subevent_packets : 32,
+  retransmitted_packets : 32,
+  crc_error_packets : 32,
+  rx_unreceived_packets : 32,
+  duplicate_packets : 32,
+}
+
+packet LeEnhancedReadTransmitPowerLevel : CommandPacket (op_code = LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  phy : 8,
+}
+
+enum PhyWithCodedSpecified : 8 {
+  LE_1M = 1,
+  LE_2M = 2,
+  LE_CODED_S_8 = 3,
+  LE_CODED_S_2 = 4,
+}
+
+packet LeEnhancedReadTransmitPowerLevelComplete : CommandComplete (command_op_code = LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  phy : PhyWithCodedSpecified,
+  current_transmit_power_level : 8,
+  max_transmit_power_level : 8,
+}
+
+packet LeReadRemoteTransmitPowerLevel : CommandPacket (op_code = LE_READ_REMOTE_TRANSMIT_POWER_LEVEL) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  phy : 8,
+}
+
+packet LeReadRemoteTransmitPowerLevelStatus : CommandStatus (command_op_code = LE_READ_REMOTE_TRANSMIT_POWER_LEVEL) {
+}
+
+packet LeSetPathLossReportingParameters : CommandPacket (op_code = LE_SET_PATH_LOSS_REPORTING_PARAMETERS) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  high_threshold : 8,
+  high_hysteresis : 8,
+  low_threshold : 8,
+  low_hysteresis : 8,
+  min_time_spent : 16,
+}
+
+packet LeSetPathLossReportingParametersComplete : CommandComplete (command_op_code = LE_SET_PATH_LOSS_REPORTING_PARAMETERS) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeSetPathLossReportingEnable : CommandPacket (op_code = LE_SET_PATH_LOSS_REPORTING_ENABLE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  enable : 8,
+}
+
+packet LeSetPathLossReportingEnableComplete : CommandComplete (command_op_code = LE_SET_PATH_LOSS_REPORTING_ENABLE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeSetTransmitPowerReportingEnable : CommandPacket (op_code = LE_SET_TRANSMIT_POWER_REPORTING_ENABLE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  local_enable : 8,
+  remote_enable : 8,
+}
+
+packet LeSetTransmitPowerReportingEnableComplete : CommandComplete (command_op_code = LE_SET_TRANSMIT_POWER_REPORTING_ENABLE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
   // VENDOR_SPECIFIC
 packet LeGetVendorCapabilities : VendorCommand (op_code = LE_GET_VENDOR_CAPABILITIES) {
 }
@@ -3156,7 +3726,7 @@
 packet LeMultiAdvtParam : LeMultiAdvt (sub_cmd = SET_PARAM) {
   interval_min : 16,
   interval_max : 16,
-  type : AdvertisingEventType,
+  type : AdvertisingType,
   own_address_type : AddressType,
   peer_address_type : PeerAddressType,
   peer_address : Address,
@@ -3244,7 +3814,7 @@
   le_scan_interval : 32, // 0x0004-0x4000 Default 0x10 (10ms)
   le_scan_window : 32, // Default 0x10 (10ms)
   own_address_type : AddressType,
-  scanning_filter_policy : LeSetScanningFilterPolicy,
+  scanning_filter_policy : LeScanningFilterPolicy,
 }
 
 packet LeExtendedScanParamsComplete : CommandComplete (command_op_code = LE_EXTENDED_SCAN_PARAMS) {
@@ -3282,8 +3852,7 @@
   status : ErrorCode,
 }
 
-packet InquiryResult : EventPacket (event_code = INQUIRY_RESULT){
-  num_responses : 8,
+struct InquiryResult {
   bd_addr : Address,
   page_scan_repetition_mode : PageScanRepetitionMode,
   _reserved_ : 8,
@@ -3293,6 +3862,11 @@
   _reserved_ : 1,
 }
 
+packet InquiryResult : EventPacket (event_code = INQUIRY_RESULT){
+  _count_(inquiry_results) : 8,
+  inquiry_results : InquiryResult[],
+}
+
 enum LinkType : 8 {
   SCO = 0x00,
   ACL = 0x01,
@@ -3513,8 +4087,7 @@
   access_latency : 32, // Octets/s
 }
 
-packet InquiryResultWithRssi : EventPacket (event_code = INQUIRY_RESULT_WITH_RSSI){
-  num_responses : 8,
+struct InquiryResultWithRssi {
   address : Address,
   page_scan_repetition_mode : PageScanRepetitionMode,
   _reserved_ : 8,
@@ -3524,6 +4097,11 @@
   rssi : 8,
 }
 
+packet InquiryResultWithRssi : EventPacket (event_code = INQUIRY_RESULT_WITH_RSSI){
+  _count_(inquiry_results) : 8,
+  inquiry_results : InquiryResultWithRssi[],
+}
+
 packet ReadRemoteExtendedFeaturesComplete : EventPacket (event_code = READ_REMOTE_EXTENDED_FEATURES_COMPLETE){
   status : ErrorCode,
   connection_handle : 12,
@@ -3628,7 +4206,7 @@
 
 packet LeMetaEvent : EventPacket (event_code = LE_META_EVENT) {
   subevent_code : SubeventCode,
-  _payload_,
+  _body_,
 }
 
 packet NumberOfCompletedDataBlocks : EventPacket (event_code = NUMBER_OF_COMPLETED_DATA_BLOCKS){
@@ -3636,18 +4214,6 @@
 }
 
 // LE Events
-
-enum MasterClockAccuracy : 8 {
-  PPM_500 = 0x00,
-  PPM_250 = 0x01,
-  PPM_150 = 0x02,
-  PPM_100 = 0x03,
-  PPM_75 = 0x04,
-  PPM_50 = 0x05,
-  PPM_30 = 0x06,
-  PPM_20 = 0x07,
-}
-
 packet LeConnectionComplete : LeMetaEvent (subevent_code = CONNECTION_COMPLETE) {
   status : ErrorCode,
   connection_handle : 12,
@@ -3658,7 +4224,15 @@
   conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
   conn_latency : 16,  // Number of connection events
   supervision_timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
-  master_clock_accuracy : MasterClockAccuracy,
+  master_clock_accuracy : ClockAccuracy,
+}
+
+enum AdvertisingEventType : 8 {
+  ADV_IND = 0x00,
+  ADV_DIRECT_IND = 0x01,
+  ADV_SCAN_IND = 0x02,
+  ADV_NONCONN_IND = 0x03,
+  SCAN_RESPONSE = 0x04,
 }
 
 struct LeAdvertisingReport {
@@ -3739,7 +4313,7 @@
   conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
   conn_latency : 16,  // Number of connection events
   supervision_timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
-  master_clock_accuracy : MasterClockAccuracy,
+  master_clock_accuracy : ClockAccuracy,
 }
 
 enum DirectAdvertisingAddressType : 8 {
@@ -3840,6 +4414,166 @@
   scanner_address : Address,
 }
 
+enum ChannelSelectionAlGorithm : 8 {
+  ALGORITHM1 = 0,
+  ALGORITHM2 = 1,
+}
+
+packet LeChannelSelectionAlgorithm : LeMetaEvent (subevent_code = CHANNEL_SELECTION_ALGORITHM) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  channel_selection_algorithm : ChannelSelectionAlGorithm,
+}
+
+packet LeConnectionlessIqReport : LeMetaEvent (subevent_code = CONNECTIONLESS_IQ_REPORT) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LeConnectionIqReport : LeMetaEvent (subevent_code = CONNECTION_IQ_REPORT) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LeCteRequestFailed : LeMetaEvent (subevent_code = CTE_REQUEST_FAILED) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingSyncTransferReceived : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  service_data : 16,
+  sync_handle : 12,
+  _reserved_ : 4,
+  advertising_sid : 4,
+  _reserved_ : 4,
+  advertiser_address_type : AddressType,
+  advertiser_address : Address,
+  advertiser_phy : SecondaryPhyType,
+  periodic_advertising_interval : 16,
+  advertiser_clock_accuracy : ClockAccuracy,
+}
+
+packet LeCisEstablished : LeMetaEvent (subevent_code = CIS_ESTABLISHED) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  cig_sync_delay : 24,
+  cis_sync_delay : 24,
+  transport_latency_m_to_s : 24,
+  transport_latency_s_to_m : 24,
+  phy_m_to_s : SecondaryPhyType,
+  phy_s_to_m : SecondaryPhyType,
+  nse : 8,
+  bn_m_to_s : 4,
+  _reserved_ : 4,
+  bn_s_to_m : 4,
+  _reserved_ : 4,
+  ft_m_to_s : 8,
+  ft_s_to_m : 8,
+  max_pdu_m_to_s : 8,
+  _reserved_ : 8,
+  max_pdu_s_to_m : 8,
+  _reserved_ : 8,
+  iso_interval : 16,
+}
+
+packet LeCisRequest : LeMetaEvent (subevent_code = CIS_REQUEST) {
+  acl_connection_handle : 12,
+  _reserved_ : 4,
+  cis_connection_handle : 12,
+  _reserved_ : 4,
+  cig_id : 8,
+  cis_id : 8,
+}
+
+packet LeCreateBigComplete : LeMetaEvent (subevent_code = CREATE_BIG_COMPLETE) {
+  status : ErrorCode,
+  big_handle : 8,
+  big_sync_delay : 24,
+  transport_latency_big: 24,
+  phy : SecondaryPhyType,
+  nse : 8,
+  bn : 8,
+  pto : 8,
+  irc : 8,
+  max_pdu : 16,
+  iso_interval : 16,
+  _size_(connection_handle) : 8,
+  connection_handle : 16[],
+}
+
+packet LeTerminateBigComplete : LeMetaEvent (subevent_code = TERMINATE_BIG_COMPLETE) {
+  big_handle : 8,
+  reason : ErrorCode,
+}
+
+packet LeBigSyncEstablished : LeMetaEvent (subevent_code = BIG_SYNC_ESTABLISHED) {
+  status : ErrorCode,
+  big_handle : 8,
+  transport_latency_big : 24,
+  nse : 8,
+  bn : 8,
+  pto : 8,
+  irc : 8,
+  max_pdu : 16,
+  iso_interval : 16,
+  _size_(connection_handle) : 8,
+  connection_handle : 16[],
+}
+
+packet LeBigSyncLost : LeMetaEvent (subevent_code = BIG_SYNC_LOST) {
+  big_handle : 8,
+  reason : ErrorCode,
+}
+
+packet LeRequestPeerScaComplete : LeMetaEvent (subevent_code = REQUEST_PEER_SCA_COMPLETE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  peer_clock_accuracy : ClockAccuracy,
+}
+
+enum PathLossZone : 8 {
+  LOW = 0,
+  MID = 1,
+  HIGH = 2,
+}
+
+packet LePathLossThreshold : LeMetaEvent (subevent_code = PATH_LOSS_THRESHOLD) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  current_path_loss : 8,
+  zone_entered : PathLossZone,
+}
+
+packet LeTransmitPowerReporting : LeMetaEvent (subevent_code = TRANSMIT_POWER_REPORTING) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  reason : 8,
+  phy : 8,
+  transmit_power_level : 8,
+  transmit_power_level_flag : 8,
+  delta : 8,
+}
+
+packet LeBigInfoAdvertisingReport : LeMetaEvent (subevent_code = BIG_INFO_ADVERTISING_REPORT) {
+  sync_handle : 12,
+  _reserved_ : 4,
+  num_bis : 8,
+  nse : 8,
+  iso_interval : 16,
+  bn : 8,
+  pto : 8,
+  irc : 8,
+  max_pdu : 16,
+  sdu_interval : 24,
+  max_sdu : 16,
+  phy : SecondaryPhyType,
+  framing : Enable,
+  encryption : Enable,
+}
+
 // Vendor specific events
 
 packet VendorSpecificEvent : EventPacket (event_code = VENDOR_SPECIFIC) {
diff --git a/gd/hci/hci_packets_fuzz_test.cc b/gd/hci/hci_packets_fuzz_test.cc
index 5ef3ef6..9bc4286 100644
--- a/gd/hci/hci_packets_fuzz_test.cc
+++ b/gd/hci/hci_packets_fuzz_test.cc
@@ -72,13 +72,13 @@
 
 DEFINE_AND_REGISTER_WriteSecureConnectionsHostSupportCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
 
-DEFINE_AND_REGISTER_LeReadWhiteListSizeReflectionFuzzTest(hci_packet_fuzz_tests);
+DEFINE_AND_REGISTER_LeReadConnectListSizeReflectionFuzzTest(hci_packet_fuzz_tests);
 
-DEFINE_AND_REGISTER_LeReadWhiteListSizeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+DEFINE_AND_REGISTER_LeReadConnectListSizeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
 
-DEFINE_AND_REGISTER_LeReadBufferSizeReflectionFuzzTest(hci_packet_fuzz_tests);
+DEFINE_AND_REGISTER_LeReadBufferSizeV1ReflectionFuzzTest(hci_packet_fuzz_tests);
 
-DEFINE_AND_REGISTER_LeReadBufferSizeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+DEFINE_AND_REGISTER_LeReadBufferSizeV1CompleteReflectionFuzzTest(hci_packet_fuzz_tests);
 
 DEFINE_AND_REGISTER_WriteCurrentIacLapReflectionFuzzTest(hci_packet_fuzz_tests);
 
@@ -132,4 +132,4 @@
   for (auto test_function : bluetooth::hci::hci_packet_fuzz_tests) {
     test_function(data, size);
   }
-}
\ No newline at end of file
+}
diff --git a/gd/hci/hci_packets_test.cc b/gd/hci/hci_packets_test.cc
index ad00162..84b1b6d 100644
--- a/gd/hci/hci_packets_test.cc
+++ b/gd/hci/hci_packets_test.cc
@@ -115,17 +115,17 @@
 DEFINE_AND_INSTANTIATE_WriteSecureConnectionsHostSupportCompleteReflectionTest(
     write_secure_connections_host_support_complete);
 
-std::vector<uint8_t> le_read_white_list_size = {0x0f, 0x20, 0x00};
-DEFINE_AND_INSTANTIATE_LeReadWhiteListSizeReflectionTest(le_read_white_list_size);
+std::vector<uint8_t> le_read_connect_list_size = {0x0f, 0x20, 0x00};
+DEFINE_AND_INSTANTIATE_LeReadConnectListSizeReflectionTest(le_read_connect_list_size);
 
-std::vector<uint8_t> le_read_white_list_size_complete = {0x0e, 0x05, 0x01, 0x0f, 0x20, 0x00, 0x80};
-DEFINE_AND_INSTANTIATE_LeReadWhiteListSizeCompleteReflectionTest(le_read_white_list_size_complete);
+std::vector<uint8_t> le_read_connect_list_size_complete = {0x0e, 0x05, 0x01, 0x0f, 0x20, 0x00, 0x80};
+DEFINE_AND_INSTANTIATE_LeReadConnectListSizeCompleteReflectionTest(le_read_connect_list_size_complete);
 
 std::vector<uint8_t> le_read_buffer_size = {0x02, 0x20, 0x00};
-DEFINE_AND_INSTANTIATE_LeReadBufferSizeReflectionTest(le_read_buffer_size);
+DEFINE_AND_INSTANTIATE_LeReadBufferSizeV1ReflectionTest(le_read_buffer_size);
 
 std::vector<uint8_t> le_read_buffer_size_complete = {0x0e, 0x07, 0x01, 0x02, 0x20, 0x00, 0xfb, 0x00, 0x10};
-DEFINE_AND_INSTANTIATE_LeReadBufferSizeCompleteReflectionTest(le_read_buffer_size_complete);
+DEFINE_AND_INSTANTIATE_LeReadBufferSizeV1CompleteReflectionTest(le_read_buffer_size_complete);
 
 std::vector<uint8_t> write_current_iac_laps = {0x3a, 0x0c, 0x07, 0x02, 0x11, 0x8b, 0x9e, 0x22, 0x8b, 0x9e};
 DEFINE_AND_INSTANTIATE_WriteCurrentIacLapReflectionTest(write_current_iac_laps);
@@ -271,12 +271,12 @@
   auto view =
       LeSetScanParametersView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
 
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
   ASSERT_EQ(LeScanType::ACTIVE, view.GetLeScanType());
   ASSERT_EQ(0x12, view.GetLeScanInterval());
   ASSERT_EQ(0x12, view.GetLeScanWindow());
   ASSERT_EQ(AddressType::RANDOM_DEVICE_ADDRESS, view.GetOwnAddressType());
-  ASSERT_EQ(LeSetScanningFilterPolicy::ACCEPT_ALL, view.GetScanningFilterPolicy());
+  ASSERT_EQ(LeScanningFilterPolicy::ACCEPT_ALL, view.GetScanningFilterPolicy());
 }
 
 DEFINE_AND_INSTANTIATE_LeSetScanParametersReflectionTest(le_set_scan_parameters);
@@ -288,7 +288,7 @@
   PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_set_scan_enable));
   auto view = LeSetScanEnableView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
 
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
   ASSERT_EQ(Enable::ENABLED, view.GetLeScanEnable());
   ASSERT_EQ(Enable::DISABLED, view.GetFilterDuplicates());
 }
@@ -305,7 +305,7 @@
   auto view =
       LeGetVendorCapabilitiesView::Create(VendorCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
 
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
 }
 
 DEFINE_AND_INSTANTIATE_LeGetVendorCapabilitiesReflectionTest(le_get_vendor_capabilities);
@@ -319,7 +319,7 @@
   auto view = LeGetVendorCapabilitiesCompleteView::Create(
       CommandCompleteView::Create(EventPacketView::Create(packet_bytes_view)));
 
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
   auto base_capabilities = view.GetBaseVendorCapabilities();
   ASSERT_EQ(5, base_capabilities.max_advt_instances_);
   ASSERT_EQ(1, base_capabilities.offloaded_resolution_of_private_address_);
@@ -341,7 +341,7 @@
   auto view = LeSetExtendedScanParametersView::Create(
       LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
 
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
   ASSERT_EQ(1, view.GetScanningPhys());
   auto params = view.GetParameters();
   ASSERT_EQ(1, params.size());
@@ -360,7 +360,7 @@
   auto view = LeSetExtendedScanParametersView::Create(
       LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
 
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
   ASSERT_EQ(1, view.GetScanningPhys());
   auto params = view.GetParameters();
   ASSERT_EQ(1, params.size());
@@ -386,7 +386,7 @@
   auto view =
       LeSetExtendedScanEnableView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
 
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
   ASSERT_EQ(FilterDuplicates::DISABLED, view.GetFilterDuplicates());
   ASSERT_EQ(Enable::ENABLED, view.GetEnable());
   ASSERT_EQ(0, view.GetDuration());
@@ -403,7 +403,7 @@
   auto view =
       LeSetExtendedScanEnableView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
 
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
   ASSERT_EQ(FilterDuplicates::ENABLED, view.GetFilterDuplicates());
   ASSERT_EQ(Enable::DISABLED, view.GetEnable());
   ASSERT_EQ(0, view.GetDuration());
@@ -468,7 +468,7 @@
       LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
   ASSERT_TRUE(view.IsValid());
   ASSERT_EQ(0, view.GetAdvertisingHandle());
-  ASSERT_EQ(Operation::COMPLETE_ADVERTISMENT, view.GetOperation());
+  ASSERT_EQ(Operation::COMPLETE_ADVERTISEMENT, view.GetOperation());
   ASSERT_EQ(FragmentPreference::CONTROLLER_SHOULD_NOT, view.GetFragmentPreference());
   std::vector<uint8_t> advertising_data{
       0x02, 0x01, 0x02, 0x0a, 0x09, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x33, 0x20, 0x58,
@@ -484,8 +484,8 @@
 DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingDataCompleteReflectionTest(le_set_extended_advertising_data_complete);
 
 std::vector<uint8_t> le_set_extended_advertising_parameters_set_0{
-    0x36, 0x20, 0x19, 0x00, 0x13, 0x00, 0x90, 0x01, 0x00, 0xc2, 0x01, 0x00,          0x07, 0x01,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x00 /*0x01*/, 0x01, 0x00,
+    0x36, 0x20, 0x19, 0x00, 0x13, 0x00, 0x90, 0x01, 0x00, 0xc2, 0x01, 0x00, 0x07, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x01, 0x01, 0x00,
 };
 TEST(HciPacketsTest, testLeSetExtendedAdvertisingParametersLegacySet0) {
   std::shared_ptr<std::vector<uint8_t>> packet_bytes =
@@ -502,14 +502,13 @@
   ASSERT_EQ(PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, view.GetPeerAddressType());
   ASSERT_EQ(Address::kEmpty, view.GetPeerAddress());
   ASSERT_EQ(AdvertisingFilterPolicy::ALL_DEVICES, view.GetAdvertisingFilterPolicy());
-  ASSERT_EQ(PrimaryPhyType::LE_1M, view.GetPrimaryAdvertisingPhy());
   ASSERT_EQ(1, view.GetAdvertisingSid());
   ASSERT_EQ(Enable::DISABLED, view.GetScanRequestNotificationEnable());
 }
 
 std::vector<uint8_t> le_set_extended_advertising_parameters_set_1{
-    0x36, 0x20, 0x19, 0x01, 0x13, 0x00, 0x90, 0x01, 0x00, 0xc2, 0x01, 0x00,          0x07, 0x01,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x00 /*0x01*/, 0x01, 0x00,
+    0x36, 0x20, 0x19, 0x01, 0x13, 0x00, 0x90, 0x01, 0x00, 0xc2, 0x01, 0x00, 0x07, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x01, 0x01, 0x00,
 };
 TEST(HciPacketsTest, testLeSetExtendedAdvertisingParametersSet1) {
   std::shared_ptr<std::vector<uint8_t>> packet_bytes =
@@ -526,7 +525,6 @@
   ASSERT_EQ(PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, view.GetPeerAddressType());
   ASSERT_EQ(Address::kEmpty, view.GetPeerAddress());
   ASSERT_EQ(AdvertisingFilterPolicy::ALL_DEVICES, view.GetAdvertisingFilterPolicy());
-  ASSERT_EQ(PrimaryPhyType::LE_1M, view.GetPrimaryAdvertisingPhy());
   ASSERT_EQ(1, view.GetAdvertisingSid());
   ASSERT_EQ(Enable::DISABLED, view.GetScanRequestNotificationEnable());
 }
@@ -606,10 +604,10 @@
   BitInserter bit_inserter(*packet_bytes);
   builder->Serialize(bit_inserter);
   auto command_view = LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes));
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
   ASSERT_EQ(1 /* data_length */ + 31 /* data */, command_view.GetPayload().size());
   auto view = LeSetAdvertisingDataView::Create(command_view);
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
 }
 
 TEST(HciPacketsTest, testLeSetScanResponseDataBuilderLength) {
@@ -624,10 +622,10 @@
   BitInserter bit_inserter(*packet_bytes);
   builder->Serialize(bit_inserter);
   auto command_view = LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes));
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
   ASSERT_EQ(1 /* data_length */ + 31 /* data */, command_view.GetPayload().size());
   auto view = LeSetScanResponseDataView::Create(command_view);
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
 }
 
 TEST(HciPacketsTest, testLeMultiAdvSetAdvertisingDataBuilderLength) {
@@ -644,10 +642,10 @@
   builder->Serialize(bit_inserter);
   auto command_view =
       LeMultiAdvtView::Create(LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes)));
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
   EXPECT_EQ(1 /* data_length */ + 31 /* data */ + 1 /* set */, command_view.GetPayload().size());
   auto view = LeMultiAdvtSetDataView::Create(command_view);
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
 }
 
 TEST(HciPacketsTest, testLeMultiAdvSetScanResponseDataBuilderLength) {
@@ -664,10 +662,10 @@
   builder->Serialize(bit_inserter);
   auto command_view =
       LeMultiAdvtView::Create(LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes)));
-  ASSERT(command_view.IsValid());
+  ASSERT_TRUE(command_view.IsValid());
   ASSERT_EQ(1 /* data_length */ + 31 /* data */ + 1 /* set */, command_view.GetPayload().size());
   auto view = LeMultiAdvtSetScanRespView::Create(command_view);
-  ASSERT(view.IsValid());
+  ASSERT_TRUE(view.IsValid());
 }
 
 std::vector<uint8_t> controller_bqr = {0x5e, 0xfd, 0x07, 0x00, 0x1f, 0x00, 0x07, 0x00, 0x88, 0x13};
diff --git a/gd/hci/le_acl_connection_interface.h b/gd/hci/le_acl_connection_interface.h
new file mode 100644
index 0000000..7947482a
--- /dev/null
+++ b/gd/hci/le_acl_connection_interface.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "hci/command_interface.h"
+#include "hci/hci_packets.h"
+
+namespace bluetooth {
+namespace hci {
+
+constexpr SubeventCode LeConnectionManagementEvents[] = {
+    SubeventCode::CONNECTION_COMPLETE,
+    SubeventCode::ENHANCED_CONNECTION_COMPLETE,
+    SubeventCode::CONNECTION_UPDATE_COMPLETE,
+};
+
+typedef CommandInterface<LeConnectionManagementCommandBuilder> LeAclConnectionInterface;
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_address_manager.cc b/gd/hci/le_address_manager.cc
new file mode 100644
index 0000000..1721a09
--- /dev/null
+++ b/gd/hci/le_address_manager.cc
@@ -0,0 +1,355 @@
+#include "hci/le_address_manager.h"
+#include "os/log.h"
+#include "os/rand.h"
+
+namespace bluetooth {
+namespace hci {
+
+static constexpr uint8_t BLE_ADDR_MASK = 0xc0u;
+
+LeAddressManager::LeAddressManager(
+    common::Callback<void(std::unique_ptr<CommandPacketBuilder>)> enqueue_command,
+    os::Handler* handler,
+    Address public_address,
+    uint8_t connect_list_size,
+    uint8_t resolving_list_size)
+    : enqueue_command_(enqueue_command),
+      handler_(handler),
+      public_address_(public_address),
+      connect_list_size_(connect_list_size),
+      resolving_list_size_(resolving_list_size){};
+
+LeAddressManager::~LeAddressManager() {
+  if (address_rotation_alarm_ != nullptr) {
+    address_rotation_alarm_->Cancel();
+    address_rotation_alarm_.reset();
+  }
+}
+
+// Aborts if called more than once
+void LeAddressManager::SetPrivacyPolicyForInitiatorAddress(
+    AddressPolicy address_policy,
+    AddressWithType fixed_address,
+    crypto_toolbox::Octet16 rotation_irk,
+    std::chrono::milliseconds minimum_rotation_time,
+    std::chrono::milliseconds maximum_rotation_time) {
+  ASSERT(address_policy_ == AddressPolicy::POLICY_NOT_SET);
+  ASSERT(address_policy != AddressPolicy::POLICY_NOT_SET);
+  ASSERT_LOG(registered_clients_.empty(), "Policy must be set before clients are registered.");
+  address_policy_ = address_policy;
+
+  switch (address_policy_) {
+    case AddressPolicy::USE_PUBLIC_ADDRESS:
+      le_address_ = fixed_address;
+      break;
+    case AddressPolicy::USE_STATIC_ADDRESS: {
+      auto addr = fixed_address.GetAddress();
+      auto address = addr.address;
+      // The two most significant bits of the static address shall be equal to 1
+      ASSERT_LOG((address[5] & BLE_ADDR_MASK) == BLE_ADDR_MASK, "The two most significant bits shall be equal to 1");
+      // Bits of the random part of the address shall not be all 1 or all 0
+      if ((address[0] == 0x00 && address[1] == 0x00 && address[2] == 0x00 && address[3] == 0x00 && address[4] == 0x00 &&
+           address[5] == BLE_ADDR_MASK) ||
+          (address[0] == 0xFF && address[1] == 0xFF && address[2] == 0xFF && address[3] == 0xFF && address[4] == 0xFF &&
+           address[5] == 0xFF)) {
+        LOG_ALWAYS_FATAL("Bits of the random part of the address shall not be all 1 or all 0");
+      }
+      le_address_ = fixed_address;
+      auto packet = hci::LeSetRandomAddressBuilder::Create(le_address_.GetAddress());
+      handler_->Post(common::BindOnce(enqueue_command_, std::move(packet)));
+    } break;
+    case AddressPolicy::USE_NON_RESOLVABLE_ADDRESS:
+    case AddressPolicy::USE_RESOLVABLE_ADDRESS:
+      rotation_irk_ = rotation_irk;
+      minimum_rotation_time_ = minimum_rotation_time;
+      maximum_rotation_time_ = maximum_rotation_time;
+      address_rotation_alarm_ = std::make_unique<os::Alarm>(handler_);
+      break;
+    case AddressPolicy::POLICY_NOT_SET:
+      LOG_ALWAYS_FATAL("invalid parameters");
+  }
+}
+
+LeAddressManager::AddressPolicy LeAddressManager::Register(LeAddressManagerCallback* callback) {
+  handler_->BindOnceOn(this, &LeAddressManager::register_client, callback).Invoke();
+  return address_policy_;
+}
+
+void LeAddressManager::register_client(LeAddressManagerCallback* callback) {
+  registered_clients_.insert(std::pair<LeAddressManagerCallback*, ClientState>(callback, ClientState::RESUMED));
+  if (address_policy_ == AddressPolicy::POLICY_NOT_SET || address_policy_ == AddressPolicy::USE_RESOLVABLE_ADDRESS ||
+      address_policy_ == AddressPolicy::USE_NON_RESOLVABLE_ADDRESS) {
+    prepare_to_rotate();
+  }
+}
+
+void LeAddressManager::Unregister(LeAddressManagerCallback* callback) {
+  handler_->BindOnceOn(this, &LeAddressManager::unregister_client, callback).Invoke();
+}
+
+void LeAddressManager::unregister_client(LeAddressManagerCallback* callback) {
+  registered_clients_.erase(callback);
+  if (registered_clients_.empty() && address_rotation_alarm_ != nullptr) {
+    address_rotation_alarm_->Cancel();
+    address_rotation_alarm_.reset();
+  }
+}
+
+void LeAddressManager::AckPause(LeAddressManagerCallback* callback) {
+  handler_->BindOnceOn(this, &LeAddressManager::ack_pause, callback).Invoke();
+}
+
+void LeAddressManager::AckResume(LeAddressManagerCallback* callback) {
+  handler_->BindOnceOn(this, &LeAddressManager::ack_resume, callback).Invoke();
+}
+
+AddressWithType LeAddressManager::GetCurrentAddress() {
+  ASSERT(address_policy_ != AddressPolicy::POLICY_NOT_SET);
+  return le_address_;
+}
+
+AddressWithType LeAddressManager::GetAnotherAddress() {
+  ASSERT(
+      address_policy_ == AddressPolicy::USE_NON_RESOLVABLE_ADDRESS ||
+      address_policy_ == AddressPolicy::USE_RESOLVABLE_ADDRESS);
+  hci::Address address = generate_rpa();
+  auto random_address = AddressWithType(address, AddressType::RANDOM_DEVICE_ADDRESS);
+  return random_address;
+}
+
+void LeAddressManager::pause_registered_clients() {
+  for (auto client : registered_clients_) {
+    if (client.second != ClientState::PAUSED && client.second != ClientState::WAITING_FOR_PAUSE) {
+      client.second = ClientState::WAITING_FOR_PAUSE;
+      client.first->OnPause();
+    }
+  }
+}
+
+void LeAddressManager::ack_pause(LeAddressManagerCallback* callback) {
+  ASSERT(registered_clients_.find(callback) != registered_clients_.end());
+  registered_clients_.find(callback)->second = ClientState::PAUSED;
+  for (auto client : registered_clients_) {
+    if (client.second != ClientState::PAUSED) {
+      // make sure all client paused
+      return;
+    }
+  }
+  handle_next_command();
+}
+
+void LeAddressManager::resume_registered_clients() {
+  // Do not resume clients if cached command is not empty
+  if (!cached_commands_.empty()) {
+    handle_next_command();
+    return;
+  }
+
+  for (auto client : registered_clients_) {
+    client.second = ClientState::WAITING_FOR_RESUME;
+    client.first->OnResume();
+  }
+}
+
+void LeAddressManager::ack_resume(LeAddressManagerCallback* callback) {
+  ASSERT(registered_clients_.find(callback) != registered_clients_.end());
+  registered_clients_.find(callback)->second = ClientState::RESUMED;
+}
+
+void LeAddressManager::prepare_to_rotate() {
+  Command command = {CommandType::ROTATE_RANDOM_ADDRESS, nullptr};
+  cached_commands_.push(std::move(command));
+  pause_registered_clients();
+}
+
+void LeAddressManager::rotate_random_address() {
+  if (address_policy_ != AddressPolicy::USE_RESOLVABLE_ADDRESS &&
+      address_policy_ != AddressPolicy::USE_NON_RESOLVABLE_ADDRESS) {
+    return;
+  }
+
+  address_rotation_alarm_->Schedule(
+      common::BindOnce(&LeAddressManager::prepare_to_rotate, common::Unretained(this)),
+      get_next_private_address_interval_ms());
+
+  hci::Address address;
+  if (address_policy_ == AddressPolicy::USE_RESOLVABLE_ADDRESS) {
+    address = generate_rpa();
+  } else {
+    address = generate_nrpa();
+  }
+  auto packet = hci::LeSetRandomAddressBuilder::Create(address);
+  enqueue_command_.Run(std::move(packet));
+  le_address_ = AddressWithType(address, AddressType::RANDOM_DEVICE_ADDRESS);
+}
+
+void LeAddressManager::on_le_set_random_address_complete(CommandCompleteView view) {
+  auto complete_view = LeSetRandomAddressCompleteView::Create(view);
+  if (!complete_view.IsValid()) {
+    LOG_ALWAYS_FATAL("Received on_le_set_random_address_complete with invalid packet");
+  } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+    auto status = complete_view.GetStatus();
+    std::string error_code = ErrorCodeText(status);
+    LOG_ALWAYS_FATAL("Received on_le_set_random_address_complete with error code %s", error_code.c_str());
+  }
+  if (cached_commands_.empty()) {
+    handler_->BindOnceOn(this, &LeAddressManager::resume_registered_clients).Invoke();
+  } else {
+    handler_->BindOnceOn(this, &LeAddressManager::handle_next_command).Invoke();
+  }
+}
+
+/* This function generates Resolvable Private Address (RPA) from Identity
+ * Resolving Key |irk| and |prand|*/
+hci::Address LeAddressManager::generate_rpa() {
+  // most significant bit, bit7, bit6 is 01 to be resolvable random
+  // Bits of the random part of prand shall not be all 1 or all 0
+  std::array<uint8_t, 3> prand = os::GenerateRandom<3>();
+  constexpr uint8_t BLE_RESOLVE_ADDR_MSB = 0x40;
+  prand[2] &= ~BLE_ADDR_MASK;
+  if ((prand[0] == 0x00 && prand[1] == 0x00 && prand[2] == 0x00) ||
+      (prand[0] == 0xFF && prand[1] == 0xFF && prand[2] == 0x3F)) {
+    prand[0] = (uint8_t)(os::GenerateRandom() % 0xFE + 1);
+  }
+  prand[2] |= BLE_RESOLVE_ADDR_MSB;
+
+  hci::Address address;
+  address.address[3] = prand[0];
+  address.address[4] = prand[1];
+  address.address[5] = prand[2];
+
+  /* encrypt with IRK */
+  crypto_toolbox::Octet16 p = crypto_toolbox::aes_128(rotation_irk_, prand.data(), 3);
+
+  /* set hash to be LSB of rpAddress */
+  address.address[0] = p[0];
+  address.address[1] = p[1];
+  address.address[2] = p[2];
+  return address;
+}
+
+// This function generates NON-Resolvable Private Address (NRPA)
+hci::Address LeAddressManager::generate_nrpa() {
+  // The two most significant bits of the address shall be equal to 0
+  // Bits of the random part of the address shall not be all 1 or all 0
+  std::array<uint8_t, 6> random = os::GenerateRandom<6>();
+  random[5] &= ~BLE_ADDR_MASK;
+  if ((random[0] == 0x00 && random[1] == 0x00 && random[2] == 0x00 && random[3] == 0x00 && random[4] == 0x00 &&
+       random[5] == 0x00) ||
+      (random[0] == 0xFF && random[1] == 0xFF && random[2] == 0xFF && random[3] == 0xFF && random[4] == 0xFF &&
+       random[5] == 0x3F)) {
+    random[0] = (uint8_t)(os::GenerateRandom() % 0xFE + 1);
+  }
+
+  hci::Address address;
+  address.FromOctets(random.data());
+
+  // the address shall not be equal to the public address
+  while (address == public_address_) {
+    address.address[0] = (uint8_t)(os::GenerateRandom() % 0xFE + 1);
+  }
+
+  return address;
+}
+
+std::chrono::milliseconds LeAddressManager::get_next_private_address_interval_ms() {
+  auto interval_random_part_max_ms = maximum_rotation_time_ - minimum_rotation_time_;
+  auto random_ms = std::chrono::milliseconds(os::GenerateRandom()) % (interval_random_part_max_ms);
+  return minimum_rotation_time_ + random_ms;
+}
+
+uint8_t LeAddressManager::GetConnectListSize() {
+  return connect_list_size_;
+}
+
+uint8_t LeAddressManager::GetResolvingListSize() {
+  return resolving_list_size_;
+}
+
+void LeAddressManager::handle_next_command() {
+  ASSERT(!cached_commands_.empty());
+  auto command = std::move(cached_commands_.front());
+  cached_commands_.pop();
+
+  if (command.command_type == CommandType::ROTATE_RANDOM_ADDRESS) {
+    rotate_random_address();
+  } else {
+    enqueue_command_.Run(std::move(command.command_packet));
+  }
+}
+
+void LeAddressManager::AddDeviceToConnectList(
+    ConnectListAddressType connect_list_address_type, bluetooth::hci::Address address) {
+  auto packet_builder = hci::LeAddDeviceToConnectListBuilder::Create(connect_list_address_type, address);
+  Command command = {CommandType::ADD_DEVICE_TO_CONNECT_LIST, std::move(packet_builder)};
+  handler_->BindOnceOn(this, &LeAddressManager::pause_registered_clients).Invoke();
+  cached_commands_.push(std::move(command));
+}
+
+void LeAddressManager::AddDeviceToResolvingList(
+    PeerAddressType peer_identity_address_type,
+    Address peer_identity_address,
+    const std::array<uint8_t, 16>& peer_irk,
+    const std::array<uint8_t, 16>& local_irk) {
+  auto packet_builder = hci::LeAddDeviceToResolvingListBuilder::Create(
+      peer_identity_address_type, peer_identity_address, peer_irk, local_irk);
+  Command command = {CommandType::ADD_DEVICE_TO_RESOLVING_LIST, std::move(packet_builder)};
+  handler_->BindOnceOn(this, &LeAddressManager::pause_registered_clients).Invoke();
+  cached_commands_.push(std::move(command));
+}
+
+void LeAddressManager::RemoveDeviceFromConnectList(
+    ConnectListAddressType connect_list_address_type, bluetooth::hci::Address address) {
+  auto packet_builder = hci::LeRemoveDeviceFromConnectListBuilder::Create(connect_list_address_type, address);
+  Command command = {CommandType::REMOVE_DEVICE_FROM_CONNECT_LIST, std::move(packet_builder)};
+  handler_->BindOnceOn(this, &LeAddressManager::pause_registered_clients).Invoke();
+  cached_commands_.push(std::move(command));
+}
+
+void LeAddressManager::RemoveDeviceFromResolvingList(
+    PeerAddressType peer_identity_address_type, Address peer_identity_address) {
+  auto packet_builder =
+      hci::LeRemoveDeviceFromResolvingListBuilder::Create(peer_identity_address_type, peer_identity_address);
+  Command command = {CommandType::REMOVE_DEVICE_FROM_RESOLVING_LIST, std::move(packet_builder)};
+  handler_->BindOnceOn(this, &LeAddressManager::pause_registered_clients).Invoke();
+  cached_commands_.push(std::move(command));
+}
+
+void LeAddressManager::ClearConnectList() {
+  auto packet_builder = hci::LeClearConnectListBuilder::Create();
+  Command command = {CommandType::CLEAR_CONNECT_LIST, std::move(packet_builder)};
+  handler_->BindOnceOn(this, &LeAddressManager::pause_registered_clients).Invoke();
+  cached_commands_.push(std::move(command));
+}
+
+void LeAddressManager::ClearResolvingList() {
+  auto packet_builder = hci::LeClearResolvingListBuilder::Create();
+  Command command = {CommandType::CLEAR_RESOLVING_LIST, std::move(packet_builder)};
+  handler_->BindOnceOn(this, &LeAddressManager::pause_registered_clients).Invoke();
+  cached_commands_.push(std::move(command));
+}
+
+void LeAddressManager::OnCommandComplete(bluetooth::hci::CommandCompleteView view) {
+  if (!view.IsValid()) {
+    LOG_ERROR("Received command complete with invalid packet");
+    return;
+  }
+  std::string op_code = OpCodeText(view.GetCommandOpCode());
+  LOG_DEBUG("Received command complete with op_code %s", op_code.c_str());
+
+  // The command was sent before any client registered, we can make sure all the clients paused when command complete.
+  if (view.GetCommandOpCode() == OpCode::LE_SET_RANDOM_ADDRESS &&
+      address_policy_ == AddressPolicy::USE_STATIC_ADDRESS) {
+    LOG_DEBUG("Received LE_SET_RANDOM_ADDRESS complete and Address policy is USE_STATIC_ADDRESS, return");
+    return;
+  }
+
+  if (cached_commands_.empty()) {
+    handler_->BindOnceOn(this, &LeAddressManager::resume_registered_clients).Invoke();
+  } else {
+    handler_->BindOnceOn(this, &LeAddressManager::handle_next_command).Invoke();
+  }
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_address_manager.h b/gd/hci/le_address_manager.h
new file mode 100644
index 0000000..2d15d08
--- /dev/null
+++ b/gd/hci/le_address_manager.h
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <map>
+#include <mutex>
+#include <set>
+
+#include "common/callback.h"
+#include "hci/address_with_type.h"
+#include "hci/hci_layer.h"
+#include "os/alarm.h"
+
+namespace bluetooth {
+namespace hci {
+
+class LeAddressManagerCallback {
+ public:
+  virtual ~LeAddressManagerCallback() = default;
+  virtual void OnPause() = 0;
+  virtual void OnResume() = 0;
+};
+
+class LeAddressManager {
+ public:
+  LeAddressManager(
+      common::Callback<void(std::unique_ptr<CommandPacketBuilder>)> enqueue_command,
+      os::Handler* handler,
+      Address public_address,
+      uint8_t connect_list_size,
+      uint8_t resolving_list_size);
+  virtual ~LeAddressManager();
+
+  enum AddressPolicy {
+    POLICY_NOT_SET,
+    USE_PUBLIC_ADDRESS,
+    USE_STATIC_ADDRESS,
+    USE_NON_RESOLVABLE_ADDRESS,
+    USE_RESOLVABLE_ADDRESS
+  };
+
+  // Aborts if called more than once
+  void SetPrivacyPolicyForInitiatorAddress(
+      AddressPolicy address_policy,
+      AddressWithType fixed_address,
+      crypto_toolbox::Octet16 rotation_irk,
+      std::chrono::milliseconds minimum_rotation_time,
+      std::chrono::milliseconds maximum_rotation_time);
+  void AckPause(LeAddressManagerCallback* callback);
+  void AckResume(LeAddressManagerCallback* callback);
+  virtual AddressPolicy Register(LeAddressManagerCallback* callback);
+  virtual void Unregister(LeAddressManagerCallback* callback);
+  AddressWithType GetCurrentAddress();          // What was set in SetRandomAddress()
+  virtual AddressWithType GetAnotherAddress();  // A new random address without rotating.
+
+  uint8_t GetConnectListSize();
+  uint8_t GetResolvingListSize();
+  void AddDeviceToConnectList(ConnectListAddressType connect_list_address_type, Address address);
+  void AddDeviceToResolvingList(
+      PeerAddressType peer_identity_address_type,
+      Address peer_identity_address,
+      const std::array<uint8_t, 16>& peer_irk,
+      const std::array<uint8_t, 16>& local_irk);
+  void RemoveDeviceFromConnectList(ConnectListAddressType connect_list_address_type, Address address);
+  void RemoveDeviceFromResolvingList(PeerAddressType peer_identity_address_type, Address peer_identity_address);
+  void ClearConnectList();
+  void ClearResolvingList();
+  void OnCommandComplete(CommandCompleteView view);
+
+ private:
+  void pause_registered_clients();
+  void ack_pause(LeAddressManagerCallback* callback);
+  void resume_registered_clients();
+  void ack_resume(LeAddressManagerCallback* callback);
+  void register_client(LeAddressManagerCallback* callback);
+  void unregister_client(LeAddressManagerCallback* callback);
+  void prepare_to_rotate();
+  void rotate_random_address();
+  void on_le_set_random_address_complete(CommandCompleteView view);
+  hci::Address generate_rpa();
+  hci::Address generate_nrpa();
+  std::chrono::milliseconds get_next_private_address_interval_ms();
+  void handle_next_command();
+
+  enum ClientState {
+    WAITING_FOR_PAUSE,
+    PAUSED,
+    WAITING_FOR_RESUME,
+    RESUMED,
+  };
+
+  enum CommandType {
+    ROTATE_RANDOM_ADDRESS,
+    ADD_DEVICE_TO_CONNECT_LIST,
+    REMOVE_DEVICE_FROM_CONNECT_LIST,
+    CLEAR_CONNECT_LIST,
+    ADD_DEVICE_TO_RESOLVING_LIST,
+    REMOVE_DEVICE_FROM_RESOLVING_LIST,
+    CLEAR_RESOLVING_LIST
+  };
+
+  struct Command {
+    CommandType command_type;
+    std::unique_ptr<CommandPacketBuilder> command_packet;
+  };
+
+  common::Callback<void(std::unique_ptr<CommandPacketBuilder>)> enqueue_command_;
+  os::Handler* handler_;
+  std::map<LeAddressManagerCallback*, ClientState> registered_clients_;
+
+  AddressPolicy address_policy_ = AddressPolicy::POLICY_NOT_SET;
+  AddressWithType le_address_;
+  Address public_address_;
+  std::unique_ptr<os::Alarm> address_rotation_alarm_;
+  crypto_toolbox::Octet16 rotation_irk_;
+  std::chrono::milliseconds minimum_rotation_time_;
+  std::chrono::milliseconds maximum_rotation_time_;
+  uint8_t connect_list_size_;
+  uint8_t resolving_list_size_;
+  std::queue<Command> cached_commands_;
+};
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_address_manager_test.cc b/gd/hci/le_address_manager_test.cc
new file mode 100644
index 0000000..b09abff
--- /dev/null
+++ b/gd/hci/le_address_manager_test.cc
@@ -0,0 +1,432 @@
+/*
+ * 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.
+ */
+
+#include "hci/le_address_manager.h"
+
+#include <gtest/gtest.h>
+
+#include "os/log.h"
+#include "packet/raw_builder.h"
+
+using ::bluetooth::crypto_toolbox::Octet16;
+using ::bluetooth::os::Handler;
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+namespace hci {
+
+using packet::kLittleEndian;
+using packet::PacketView;
+using packet::RawBuilder;
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+class TestHciLayer : public HciLayer {
+ public:
+  void EnqueueCommand(
+      std::unique_ptr<CommandPacketBuilder> command,
+      common::ContextualOnceCallback<void(CommandCompleteView)> on_complete) override {
+    command_queue_.push(std::move(command));
+    command_complete_callbacks.push_front(std::move(on_complete));
+    if (command_promise_ != nullptr) {
+      command_promise_->set_value();
+      command_promise_.reset();
+    }
+  }
+
+  void SetCommandFuture() {
+    ASSERT_LOG(command_promise_ == nullptr, "Promises, Promises, ... Only one at a time.");
+    command_promise_ = std::make_unique<std::promise<void>>();
+    command_future_ = std::make_unique<std::future<void>>(command_promise_->get_future());
+  }
+
+  CommandPacketView GetLastCommand() {
+    if (command_queue_.size() == 0) {
+      return CommandPacketView::Create(std::make_shared<std::vector<uint8_t>>());
+    }
+    auto last = std::move(command_queue_.front());
+    command_queue_.pop();
+    return CommandPacketView::Create(GetPacketView(std::move(last)));
+  }
+
+  CommandPacketView GetCommandPacket(OpCode op_code) {
+    if (command_future_ != nullptr) {
+      auto result = command_future_->wait_for(std::chrono::milliseconds(1000));
+      EXPECT_NE(std::future_status::timeout, result);
+    }
+    if (command_queue_.empty()) {
+      return ConnectionManagementCommandView::Create(
+          CommandPacketView::Create(std::make_shared<std::vector<uint8_t>>()));
+    }
+    CommandPacketView command_packet_view = GetLastCommand();
+    EXPECT_TRUE(command_packet_view.IsValid());
+    EXPECT_EQ(command_packet_view.GetOpCode(), op_code);
+    return command_packet_view;
+  }
+
+  void IncomingEvent(std::unique_ptr<EventPacketBuilder> event_builder) {
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    ASSERT_TRUE(event.IsValid());
+    CommandCompleteCallback(event);
+  }
+
+  void CommandCompleteCallback(EventPacketView event) {
+    CommandCompleteView complete_view = CommandCompleteView::Create(event);
+    ASSERT_TRUE(complete_view.IsValid());
+    std::move(command_complete_callbacks.front()).Invoke(complete_view);
+    command_complete_callbacks.pop_front();
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override {}
+  void Stop() override {}
+
+ private:
+  std::list<common::ContextualOnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
+  std::queue<std::unique_ptr<CommandPacketBuilder>> command_queue_;
+  std::unique_ptr<std::promise<void>> command_promise_;
+  std::unique_ptr<std::future<void>> command_future_;
+};
+
+class RotatorClient : public LeAddressManagerCallback {
+ public:
+  RotatorClient(LeAddressManager* le_address_manager, size_t id) : le_address_manager_(le_address_manager), id_(id){};
+
+  void OnPause() {
+    paused = true;
+    le_address_manager_->AckPause(this);
+  }
+
+  void OnResume() {
+    paused = false;
+    le_address_manager_->AckResume(this);
+    if (resume_promise_ != nullptr) {
+      resume_promise_->set_value();
+      resume_promise_.reset();
+    }
+  }
+
+  void WaitForResume() {
+    if (paused) {
+      resume_promise_ = std::make_unique<std::promise<void>>();
+      auto resume_future = resume_promise_->get_future();
+      auto result = resume_future.wait_for(std::chrono::milliseconds(1000));
+      EXPECT_NE(std::future_status::timeout, result);
+    }
+  }
+
+  bool paused{false};
+  LeAddressManager* le_address_manager_;
+  size_t id_;
+  std::unique_ptr<std::promise<void>> resume_promise_;
+};
+
+class LeAddressManagerTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    thread_ = new Thread("thread", Thread::Priority::NORMAL);
+    handler_ = new Handler(thread_);
+    test_hci_layer_ = new TestHciLayer;
+    Address address({0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
+    le_address_manager_ = new LeAddressManager(
+        common::Bind(&LeAddressManagerTest::enqueue_command, common::Unretained(this)), handler_, address, 0x3F, 0x3F);
+    AllocateClients(1);
+  }
+
+  void sync_handler(os::Handler* handler) {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    auto future_status = future.wait_for(std::chrono::seconds(1));
+    EXPECT_EQ(future_status, std::future_status::ready);
+  }
+
+  void TearDown() override {
+    sync_handler(handler_);
+    delete le_address_manager_;
+    delete test_hci_layer_;
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+
+  void AllocateClients(size_t num_clients) {
+    size_t first_id = clients.size();
+    for (size_t i = 0; i < num_clients; i++) {
+      clients.emplace_back(std::make_unique<RotatorClient>(le_address_manager_, first_id + i));
+    }
+  }
+
+  void enqueue_command(std::unique_ptr<CommandPacketBuilder> command_packet) {
+    test_hci_layer_->EnqueueCommand(
+        std::move(command_packet),
+        handler_->BindOnce(&LeAddressManager::OnCommandComplete, common::Unretained(le_address_manager_)));
+  }
+
+  Thread* thread_;
+  Handler* handler_;
+  TestHciLayer* test_hci_layer_ = nullptr;
+  LeAddressManager* le_address_manager_;
+  std::vector<std::unique_ptr<RotatorClient>> clients;
+};
+
+TEST_F(LeAddressManagerTest, startup_teardown) {}
+
+TEST_F(LeAddressManagerTest, register_unregister_callback) {
+  le_address_manager_->Register(clients[0].get());
+  sync_handler(handler_);
+  le_address_manager_->Unregister(clients[0].get());
+  sync_handler(handler_);
+}
+
+TEST_F(LeAddressManagerTest, rotator_address_for_single_client) {
+  Octet16 irk = {0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  auto minimum_rotation_time = std::chrono::milliseconds(1000);
+  auto maximum_rotation_time = std::chrono::milliseconds(3000);
+  AddressWithType remote_address(Address::kEmpty, AddressType::RANDOM_DEVICE_ADDRESS);
+  le_address_manager_->SetPrivacyPolicyForInitiatorAddress(
+      LeAddressManager::AddressPolicy::USE_RESOLVABLE_ADDRESS,
+      remote_address,
+      irk,
+      minimum_rotation_time,
+      maximum_rotation_time);
+
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->Register(clients[0].get());
+  sync_handler(handler_);
+  test_hci_layer_->GetCommandPacket(OpCode::LE_SET_RANDOM_ADDRESS);
+  test_hci_layer_->IncomingEvent(LeSetRandomAddressCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  clients[0].get()->WaitForResume();
+  le_address_manager_->Unregister(clients[0].get());
+  sync_handler(handler_);
+}
+
+TEST_F(LeAddressManagerTest, rotator_non_resolvable_address_for_single_client) {
+  Octet16 irk = {};
+  auto minimum_rotation_time = std::chrono::milliseconds(1000);
+  auto maximum_rotation_time = std::chrono::milliseconds(3000);
+  AddressWithType remote_address(Address::kEmpty, AddressType::RANDOM_DEVICE_ADDRESS);
+  le_address_manager_->SetPrivacyPolicyForInitiatorAddress(
+      LeAddressManager::AddressPolicy::USE_NON_RESOLVABLE_ADDRESS,
+      remote_address,
+      irk,
+      minimum_rotation_time,
+      maximum_rotation_time);
+
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->Register(clients[0].get());
+  sync_handler(handler_);
+  test_hci_layer_->GetCommandPacket(OpCode::LE_SET_RANDOM_ADDRESS);
+  test_hci_layer_->IncomingEvent(LeSetRandomAddressCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  clients[0].get()->WaitForResume();
+  le_address_manager_->Unregister(clients[0].get());
+  sync_handler(handler_);
+}
+
+// TODO handle the case "register during rotate_random_address" and enable this
+TEST_F(LeAddressManagerTest, DISABLED_rotator_address_for_multiple_clients) {
+  AllocateClients(2);
+  Octet16 irk = {0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  auto minimum_rotation_time = std::chrono::milliseconds(1000);
+  auto maximum_rotation_time = std::chrono::milliseconds(3000);
+  AddressWithType remote_address(Address::kEmpty, AddressType::RANDOM_DEVICE_ADDRESS);
+  le_address_manager_->SetPrivacyPolicyForInitiatorAddress(
+      LeAddressManager::AddressPolicy::USE_RESOLVABLE_ADDRESS,
+      remote_address,
+      irk,
+      minimum_rotation_time,
+      maximum_rotation_time);
+  le_address_manager_->Register(clients[0].get());
+  le_address_manager_->Register(clients[1].get());
+  le_address_manager_->Register(clients[2].get());
+  sync_handler(handler_);
+
+  le_address_manager_->Unregister(clients[0].get());
+  le_address_manager_->Unregister(clients[1].get());
+  le_address_manager_->Unregister(clients[2].get());
+  sync_handler(handler_);
+}
+
+class LeAddressManagerWithSingleClientTest : public LeAddressManagerTest {
+ public:
+  void SetUp() override {
+    thread_ = new Thread("thread", Thread::Priority::NORMAL);
+    handler_ = new Handler(thread_);
+    test_hci_layer_ = new TestHciLayer;
+    Address address({0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
+    le_address_manager_ = new LeAddressManager(
+        common::Bind(&LeAddressManagerWithSingleClientTest::enqueue_command, common::Unretained(this)),
+        handler_,
+        address,
+        0x3F,
+        0x3F);
+    AllocateClients(1);
+
+    Octet16 irk = {0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+    auto minimum_rotation_time = std::chrono::milliseconds(1000);
+    auto maximum_rotation_time = std::chrono::milliseconds(3000);
+    AddressWithType remote_address(Address::kEmpty, AddressType::RANDOM_DEVICE_ADDRESS);
+    le_address_manager_->SetPrivacyPolicyForInitiatorAddress(
+        LeAddressManager::AddressPolicy::USE_RESOLVABLE_ADDRESS,
+        remote_address,
+        irk,
+        minimum_rotation_time,
+        maximum_rotation_time);
+
+    test_hci_layer_->SetCommandFuture();
+    le_address_manager_->Register(clients[0].get());
+    sync_handler(handler_);
+    test_hci_layer_->GetCommandPacket(OpCode::LE_SET_RANDOM_ADDRESS);
+    test_hci_layer_->IncomingEvent(LeSetRandomAddressCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  }
+
+  void enqueue_command(std::unique_ptr<CommandPacketBuilder> command_packet) {
+    test_hci_layer_->EnqueueCommand(
+        std::move(command_packet),
+        handler_->BindOnce(&LeAddressManager::OnCommandComplete, common::Unretained(le_address_manager_)));
+  }
+
+  void TearDown() override {
+    le_address_manager_->Unregister(clients[0].get());
+    sync_handler(handler_);
+    delete le_address_manager_;
+    delete test_hci_layer_;
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+};
+
+TEST_F(LeAddressManagerWithSingleClientTest, add_device_to_connect_list) {
+  Address address;
+  Address::FromString("01:02:03:04:05:06", address);
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->AddDeviceToConnectList(ConnectListAddressType::RANDOM, address);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_ADD_DEVICE_TO_CONNECT_LIST);
+  auto packet_view = LeAddDeviceToConnectListView::Create(LeConnectionManagementCommandView::Create(packet));
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(ConnectListAddressType::RANDOM, packet_view.GetAddressType());
+  ASSERT_EQ(address, packet_view.GetAddress());
+
+  test_hci_layer_->IncomingEvent(LeAddDeviceToConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  clients[0].get()->WaitForResume();
+}
+
+TEST_F(LeAddressManagerWithSingleClientTest, remove_device_from_connect_list) {
+  Address address;
+  Address::FromString("01:02:03:04:05:06", address);
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->AddDeviceToConnectList(ConnectListAddressType::RANDOM, address);
+  test_hci_layer_->GetCommandPacket(OpCode::LE_ADD_DEVICE_TO_CONNECT_LIST);
+  test_hci_layer_->IncomingEvent(LeAddDeviceToConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->RemoveDeviceFromConnectList(ConnectListAddressType::RANDOM, address);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_REMOVE_DEVICE_FROM_CONNECT_LIST);
+  auto packet_view = LeRemoveDeviceFromConnectListView::Create(LeConnectionManagementCommandView::Create(packet));
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(ConnectListAddressType::RANDOM, packet_view.GetAddressType());
+  ASSERT_EQ(address, packet_view.GetAddress());
+  test_hci_layer_->IncomingEvent(LeRemoveDeviceFromConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  clients[0].get()->WaitForResume();
+}
+
+TEST_F(LeAddressManagerWithSingleClientTest, clear_connect_list) {
+  Address address;
+  Address::FromString("01:02:03:04:05:06", address);
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->AddDeviceToConnectList(ConnectListAddressType::RANDOM, address);
+  test_hci_layer_->GetCommandPacket(OpCode::LE_ADD_DEVICE_TO_CONNECT_LIST);
+  test_hci_layer_->IncomingEvent(LeAddDeviceToConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->ClearConnectList();
+  test_hci_layer_->GetCommandPacket(OpCode::LE_CLEAR_CONNECT_LIST);
+  test_hci_layer_->IncomingEvent(LeClearConnectListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  clients[0].get()->WaitForResume();
+}
+
+TEST_F(LeAddressManagerWithSingleClientTest, add_device_to_resolving_list) {
+  Address address;
+  Address::FromString("01:02:03:04:05:06", address);
+  Octet16 peer_irk = {0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  Octet16 local_irk = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->AddDeviceToResolvingList(
+      PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS, address, peer_irk, local_irk);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_ADD_DEVICE_TO_RESOLVING_LIST);
+  auto packet_view = LeAddDeviceToResolvingListView::Create(LeSecurityCommandView::Create(packet));
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS, packet_view.GetPeerIdentityAddressType());
+  ASSERT_EQ(address, packet_view.GetPeerIdentityAddress());
+  ASSERT_EQ(peer_irk, packet_view.GetPeerIrk());
+  ASSERT_EQ(local_irk, packet_view.GetLocalIrk());
+
+  test_hci_layer_->IncomingEvent(LeAddDeviceToResolvingListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  clients[0].get()->WaitForResume();
+}
+
+TEST_F(LeAddressManagerWithSingleClientTest, remove_device_from_resolving_list) {
+  Address address;
+  Address::FromString("01:02:03:04:05:06", address);
+  Octet16 peer_irk = {0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  Octet16 local_irk = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->AddDeviceToResolvingList(
+      PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS, address, peer_irk, local_irk);
+  test_hci_layer_->GetCommandPacket(OpCode::LE_ADD_DEVICE_TO_RESOLVING_LIST);
+  test_hci_layer_->IncomingEvent(LeAddDeviceToResolvingListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->RemoveDeviceFromResolvingList(PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS, address);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_REMOVE_DEVICE_FROM_RESOLVING_LIST);
+  auto packet_view = LeRemoveDeviceFromResolvingListView::Create(LeSecurityCommandView::Create(packet));
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS, packet_view.GetPeerIdentityAddressType());
+  ASSERT_EQ(address, packet_view.GetPeerIdentityAddress());
+  test_hci_layer_->IncomingEvent(LeRemoveDeviceFromResolvingListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  clients[0].get()->WaitForResume();
+}
+
+TEST_F(LeAddressManagerWithSingleClientTest, clear_resolving_list) {
+  Address address;
+  Address::FromString("01:02:03:04:05:06", address);
+  Octet16 peer_irk = {0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  Octet16 local_irk = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->AddDeviceToResolvingList(
+      PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS, address, peer_irk, local_irk);
+  test_hci_layer_->GetCommandPacket(OpCode::LE_ADD_DEVICE_TO_RESOLVING_LIST);
+  test_hci_layer_->IncomingEvent(LeAddDeviceToResolvingListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+
+  test_hci_layer_->SetCommandFuture();
+  le_address_manager_->ClearResolvingList();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CLEAR_RESOLVING_LIST);
+  auto packet_view = LeClearResolvingListView::Create(LeSecurityCommandView::Create(packet));
+  ASSERT_TRUE(packet_view.IsValid());
+  test_hci_layer_->IncomingEvent(LeClearResolvingListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS));
+  clients[0].get()->WaitForResume();
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_advertising_interface.h b/gd/hci/le_advertising_interface.h
index f915c02..c419c42 100644
--- a/gd/hci/le_advertising_interface.h
+++ b/gd/hci/le_advertising_interface.h
@@ -16,30 +16,18 @@
 
 #pragma once
 
-#include "common/callback.h"
+#include "hci/command_interface.h"
 #include "hci/hci_packets.h"
-#include "os/handler.h"
-#include "os/utils.h"
 
 namespace bluetooth {
 namespace hci {
 
-class LeAdvertisingInterface {
- public:
-  LeAdvertisingInterface() = default;
-  virtual ~LeAdvertisingInterface() = default;
-  DISALLOW_COPY_AND_ASSIGN(LeAdvertisingInterface);
-
-  virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
-
-  virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
-
-  static constexpr hci::SubeventCode LeAdvertisingEvents[] = {
-      hci::SubeventCode::SCAN_REQUEST_RECEIVED,
-      hci::SubeventCode::ADVERTISING_SET_TERMINATED,
-  };
+constexpr hci::SubeventCode LeAdvertisingEvents[] = {
+    hci::SubeventCode::SCAN_REQUEST_RECEIVED,
+    hci::SubeventCode::ADVERTISING_SET_TERMINATED,
 };
+
+typedef CommandInterface<LeAdvertisingCommandBuilder> LeAdvertisingInterface;
+
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/le_advertising_manager.cc b/gd/hci/le_advertising_manager.cc
index f774c35..c1e8cdd 100644
--- a/gd/hci/le_advertising_manager.cc
+++ b/gd/hci/le_advertising_manager.cc
@@ -16,6 +16,7 @@
 #include <memory>
 #include <mutex>
 
+#include "hci/acl_manager.h"
 #include "hci/controller.h"
 #include "hci/hci_layer.h"
 #include "hci/hci_packets.h"
@@ -42,15 +43,56 @@
   common::Callback<void(ErrorCode, uint8_t, uint8_t)> set_terminated_callback;
 };
 
-struct LeAdvertisingManager::impl {
+ExtendedAdvertisingConfig::ExtendedAdvertisingConfig(const AdvertisingConfig& config) : AdvertisingConfig(config) {
+  switch (config.event_type) {
+    case AdvertisingType::ADV_IND:
+      connectable = true;
+      scannable = true;
+      break;
+    case AdvertisingType::ADV_DIRECT_IND:
+      connectable = true;
+      directed = true;
+      high_duty_directed_connectable = true;
+      break;
+    case AdvertisingType::ADV_SCAN_IND:
+      scannable = true;
+      break;
+    case AdvertisingType::ADV_NONCONN_IND:
+      break;
+    case AdvertisingType::ADV_DIRECT_IND_LOW:
+      connectable = true;
+      directed = true;
+      break;
+    default:
+      LOG_WARN("Unknown event type");
+      break;
+  }
+  if (config.address_type == AddressType::PUBLIC_DEVICE_ADDRESS) {
+    own_address_type = OwnAddressType::PUBLIC_DEVICE_ADDRESS;
+  } else if (config.address_type == AddressType::RANDOM_DEVICE_ADDRESS) {
+    own_address_type = OwnAddressType::RANDOM_DEVICE_ADDRESS;
+  }
+  // TODO(b/149221472): Support fragmentation
+  operation = Operation::COMPLETE_ADVERTISEMENT;
+}
+
+struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallback {
   impl(Module* module) : module_(module), le_advertising_interface_(nullptr), num_instances_(0) {}
 
-  void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::Controller* controller) {
+  ~impl() {
+    if (address_manager_registered) {
+      le_address_manager_->Unregister(this);
+    }
+  }
+
+  void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::Controller* controller,
+             hci::AclManager* acl_manager) {
     module_handler_ = handler;
     hci_layer_ = hci_layer;
     controller_ = controller;
-    le_advertising_interface_ = hci_layer_->GetLeAdvertisingInterface(
-        common::Bind(&LeAdvertisingManager::impl::handle_event, common::Unretained(this)), module_handler_);
+    le_address_manager_ = acl_manager->GetLeAddressManager();
+    le_advertising_interface_ =
+        hci_layer_->GetLeAdvertisingInterface(module_handler_->BindOn(this, &LeAdvertisingManager::impl::handle_event));
     num_instances_ = controller_->GetControllerLeNumberOfSupportedAdverisingSets();
     enabled_sets_ = std::vector<EnabledSet>(num_instances_);
     if (controller_->IsSupported(hci::OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS)) {
@@ -119,6 +161,11 @@
       return;
     }
     advertising_sets_.erase(id);
+    if (advertising_sets_.empty() && address_manager_registered) {
+      le_address_manager_->Unregister(this);
+      address_manager_registered = false;
+      paused = false;
+    }
   }
 
   void create_advertiser(AdvertiserId id, const AdvertisingConfig& config,
@@ -128,54 +175,84 @@
     advertising_sets_[id].scan_callback = scan_callback;
     advertising_sets_[id].set_terminated_callback = set_terminated_callback;
     advertising_sets_[id].handler = handler;
+
+    if (!address_manager_registered) {
+      le_address_manager_->Register(this);
+      address_manager_registered = true;
+    }
+
     switch (advertising_api_type_) {
-      case (AdvertisingApiType::LE_4_0):
+      case (AdvertisingApiType::LE_4_0): {
         le_advertising_interface_->EnqueueCommand(
             hci::LeSetAdvertisingParametersBuilder::Create(
-                config.interval_min, config.interval_max, config.event_type, config.address_type,
-                config.peer_address_type, config.peer_address, config.channel_map, config.filter_policy),
-            common::BindOnce(impl::check_status<LeSetAdvertisingParametersCompleteView>), module_handler_);
-        le_advertising_interface_->EnqueueCommand(hci::LeSetRandomAddressBuilder::Create(config.random_address),
-                                                  common::BindOnce(impl::check_status<LeSetRandomAddressCompleteView>),
-                                                  module_handler_);
+                config.interval_min,
+                config.interval_max,
+                config.event_type,
+                config.address_type,
+                config.peer_address_type,
+                config.peer_address,
+                config.channel_map,
+                config.filter_policy),
+            module_handler_->BindOnce(impl::check_status<LeSetAdvertisingParametersCompleteView>));
         if (!config.scan_response.empty()) {
           le_advertising_interface_->EnqueueCommand(
               hci::LeSetScanResponseDataBuilder::Create(config.scan_response),
-              common::BindOnce(impl::check_status<LeSetScanResponseDataCompleteView>), module_handler_);
+              module_handler_->BindOnce(impl::check_status<LeSetScanResponseDataCompleteView>));
         }
         le_advertising_interface_->EnqueueCommand(
             hci::LeSetAdvertisingDataBuilder::Create(config.advertisement),
-            common::BindOnce(impl::check_status<LeSetAdvertisingDataCompleteView>), module_handler_);
+            module_handler_->BindOnce(impl::check_status<LeSetAdvertisingDataCompleteView>));
+        if (!paused) {
+          le_advertising_interface_->EnqueueCommand(
+              hci::LeSetAdvertisingEnableBuilder::Create(Enable::ENABLED),
+              module_handler_->BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>));
+        }
+        EnabledSet curr_set;
+        curr_set.advertising_handle_ = id;
+        enabled_sets_[id] = curr_set;
+      } break;
+      case (AdvertisingApiType::ANDROID_HCI): {
         le_advertising_interface_->EnqueueCommand(
-            hci::LeSetAdvertisingEnableBuilder::Create(Enable::ENABLED),
-            common::BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>), module_handler_);
-        break;
-      case (AdvertisingApiType::ANDROID_HCI):
+            hci::LeMultiAdvtParamBuilder::Create(
+                config.interval_min,
+                config.interval_max,
+                config.event_type,
+                config.address_type,
+                config.peer_address_type,
+                config.peer_address,
+                config.channel_map,
+                config.filter_policy,
+                id,
+                config.tx_power),
+            module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
         le_advertising_interface_->EnqueueCommand(
-            hci::LeMultiAdvtParamBuilder::Create(config.interval_min, config.interval_max, config.event_type,
-                                                 config.address_type, config.peer_address_type, config.peer_address,
-                                                 config.channel_map, config.filter_policy, id, config.tx_power),
-            common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>), module_handler_);
-        le_advertising_interface_->EnqueueCommand(hci::LeMultiAdvtSetDataBuilder::Create(config.advertisement, id),
-                                                  common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>),
-                                                  module_handler_);
+            hci::LeMultiAdvtSetDataBuilder::Create(config.advertisement, id),
+            module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
         if (!config.scan_response.empty()) {
           le_advertising_interface_->EnqueueCommand(
               hci::LeMultiAdvtSetScanRespBuilder::Create(config.scan_response, id),
-              common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>), module_handler_);
+              module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
         }
         le_advertising_interface_->EnqueueCommand(
-            hci::LeMultiAdvtSetRandomAddrBuilder::Create(config.random_address, id),
-            common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>), module_handler_);
-        le_advertising_interface_->EnqueueCommand(hci::LeMultiAdvtSetEnableBuilder::Create(Enable::ENABLED, id),
-                                                  common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>),
-                                                  module_handler_);
-        break;
+            hci::LeMultiAdvtSetRandomAddrBuilder::Create(le_address_manager_->GetAnotherAddress().GetAddress(), id),
+            module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
+        if (!paused) {
+          le_advertising_interface_->EnqueueCommand(
+              hci::LeMultiAdvtSetEnableBuilder::Create(Enable::ENABLED, id),
+              module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
+        }
+        EnabledSet curr_set;
+        curr_set.advertising_handle_ = id;
+        enabled_sets_[id] = curr_set;
+      } break;
       case (AdvertisingApiType::LE_5_0): {
-        ExtendedAdvertisingConfig new_config;
-        AdvertisingConfig* base_config_ptr = &new_config;
-        *(base_config_ptr) = config;
+        ExtendedAdvertisingConfig new_config = config;
         new_config.legacy_pdus = true;
+
+        // sid must be in range 0x00 to 0x0F. Since no controller supports more than
+        // 16 advertisers, it's safe to make sid equal to id.
+        new_config.sid = id % 0x0F;
+
         create_extended_advertiser(id, new_config, scan_callback, set_terminated_callback, handler);
       } break;
     }
@@ -190,6 +267,15 @@
       return;
     }
 
+    advertising_sets_[id].scan_callback = scan_callback;
+    advertising_sets_[id].set_terminated_callback = set_terminated_callback;
+    advertising_sets_[id].handler = handler;
+
+    if (!address_manager_registered) {
+      le_address_manager_->Register(this);
+      address_manager_registered = true;
+    }
+
     if (config.legacy_pdus) {
       LegacyAdvertisingProperties legacy_properties = LegacyAdvertisingProperties::ADV_IND;
       if (config.connectable && config.directed) {
@@ -210,9 +296,8 @@
           LeSetExtendedAdvertisingLegacyParametersBuilder::Create(
               id, legacy_properties, config.interval_min, config.interval_max, config.channel_map,
               config.own_address_type, config.peer_address_type, config.peer_address, config.filter_policy,
-              config.tx_power, (config.use_le_coded_phy ? PrimaryPhyType::LE_CODED : PrimaryPhyType::LE_1M), config.sid,
-              config.enable_scan_request_notifications),
-          common::BindOnce(impl::check_status<LeSetExtendedAdvertisingParametersCompleteView>), module_handler_);
+              config.tx_power, config.sid, config.enable_scan_request_notifications),
+          module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingParametersCompleteView>));
     } else {
       uint8_t legacy_properties = (config.connectable ? 0x1 : 0x00) | (config.scannable ? 0x2 : 0x00) |
                                   (config.directed ? 0x4 : 0x00) | (config.high_duty_directed_connectable ? 0x8 : 0x00);
@@ -225,22 +310,23 @@
               config.tx_power, (config.use_le_coded_phy ? PrimaryPhyType::LE_CODED : PrimaryPhyType::LE_1M),
               config.secondary_max_skip, config.secondary_advertising_phy, config.sid,
               config.enable_scan_request_notifications),
-          common::BindOnce(impl::check_status<LeSetExtendedAdvertisingParametersCompleteView>), module_handler_);
+          module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingParametersCompleteView>));
     }
 
     le_advertising_interface_->EnqueueCommand(
-        hci::LeSetExtendedAdvertisingRandomAddressBuilder::Create(id, config.random_address),
-        common::BindOnce(impl::check_status<LeSetExtendedAdvertisingRandomAddressCompleteView>), module_handler_);
+        hci::LeSetExtendedAdvertisingRandomAddressBuilder::Create(
+            id, le_address_manager_->GetAnotherAddress().GetAddress()),
+        module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingRandomAddressCompleteView>));
     if (!config.scan_response.empty()) {
       le_advertising_interface_->EnqueueCommand(
           hci::LeSetExtendedAdvertisingScanResponseBuilder::Create(id, config.operation, config.fragment_preference,
                                                                    config.scan_response),
-          common::BindOnce(impl::check_status<LeSetExtendedAdvertisingScanResponseCompleteView>), module_handler_);
+          module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingScanResponseCompleteView>));
     }
     le_advertising_interface_->EnqueueCommand(
         hci::LeSetExtendedAdvertisingDataBuilder::Create(id, config.operation, config.fragment_preference,
                                                          config.advertisement),
-        common::BindOnce(impl::check_status<LeSetExtendedAdvertisingDataCompleteView>), module_handler_);
+        module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingDataCompleteView>));
 
     EnabledSet curr_set;
     curr_set.advertising_handle_ = id;
@@ -249,13 +335,11 @@
     std::vector<EnabledSet> enabled_sets = {curr_set};
 
     enabled_sets_[id] = curr_set;
-    le_advertising_interface_->EnqueueCommand(
-        hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::ENABLED, enabled_sets),
-        common::BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>), module_handler_);
-
-    advertising_sets_[id].scan_callback = scan_callback;
-    advertising_sets_[id].set_terminated_callback = set_terminated_callback;
-    advertising_sets_[id].handler = handler;
+    if (!paused) {
+      le_advertising_interface_->EnqueueCommand(
+          hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::ENABLED, enabled_sets),
+          module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>));
+    }
   }
 
   void stop_advertising(AdvertiserId advertising_set) {
@@ -267,12 +351,12 @@
       case (AdvertisingApiType::LE_4_0):
         le_advertising_interface_->EnqueueCommand(
             hci::LeSetAdvertisingEnableBuilder::Create(Enable::DISABLED),
-            common::BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>), module_handler_);
+            module_handler_->BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>));
         break;
       case (AdvertisingApiType::ANDROID_HCI):
         le_advertising_interface_->EnqueueCommand(
             hci::LeMultiAdvtSetEnableBuilder::Create(Enable::DISABLED, advertising_set),
-            common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>), module_handler_);
+            module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
         break;
       case (AdvertisingApiType::LE_5_0): {
         EnabledSet curr_set;
@@ -280,13 +364,88 @@
         std::vector<EnabledSet> enabled_vector{curr_set};
         le_advertising_interface_->EnqueueCommand(
             hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::DISABLED, enabled_vector),
-            common::BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>), module_handler_);
+            module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>));
       } break;
     }
 
     std::unique_lock lock(id_mutex_);
     enabled_sets_[advertising_set].advertising_handle_ = -1;
-    advertising_sets_.erase(advertising_set);
+  }
+
+  void OnPause() override {
+    paused = true;
+    if (!advertising_sets_.empty()) {
+      switch (advertising_api_type_) {
+        case (AdvertisingApiType::LE_4_0): {
+          le_advertising_interface_->EnqueueCommand(
+              hci::LeSetAdvertisingEnableBuilder::Create(Enable::DISABLED),
+              module_handler_->BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>));
+        } break;
+        case (AdvertisingApiType::ANDROID_HCI): {
+          for (size_t i = 0; i < enabled_sets_.size(); i++) {
+            uint8_t id = enabled_sets_[i].advertising_handle_;
+            if (id != -1) {
+              le_advertising_interface_->EnqueueCommand(
+                  hci::LeMultiAdvtSetEnableBuilder::Create(Enable::DISABLED, id),
+                  module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
+            }
+          }
+        } break;
+        case (AdvertisingApiType::LE_5_0): {
+          std::vector<EnabledSet> enabled_sets = {};
+          for (size_t i = 0; i < enabled_sets_.size(); i++) {
+            EnabledSet curr_set = enabled_sets_[i];
+            if (enabled_sets_[i].advertising_handle_ != -1) {
+              enabled_sets.push_back(enabled_sets_[i]);
+            }
+          }
+          if (enabled_sets.size() != 0) {
+            le_advertising_interface_->EnqueueCommand(
+                hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::DISABLED, enabled_sets),
+                module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>));
+          }
+        } break;
+      }
+    }
+    le_address_manager_->AckPause(this);
+  }
+
+  void OnResume() override {
+    paused = false;
+    if (!advertising_sets_.empty()) {
+      switch (advertising_api_type_) {
+        case (AdvertisingApiType::LE_4_0): {
+          le_advertising_interface_->EnqueueCommand(
+              hci::LeSetAdvertisingEnableBuilder::Create(Enable::ENABLED),
+              module_handler_->BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>));
+        } break;
+        case (AdvertisingApiType::ANDROID_HCI): {
+          for (size_t i = 0; i < enabled_sets_.size(); i++) {
+            uint8_t id = enabled_sets_[i].advertising_handle_;
+            if (id != -1) {
+              le_advertising_interface_->EnqueueCommand(
+                  hci::LeMultiAdvtSetEnableBuilder::Create(Enable::ENABLED, id),
+                  module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
+            }
+          }
+        } break;
+        case (AdvertisingApiType::LE_5_0): {
+          std::vector<EnabledSet> enabled_sets = {};
+          for (size_t i = 0; i < enabled_sets_.size(); i++) {
+            EnabledSet curr_set = enabled_sets_[i];
+            if (enabled_sets_[i].advertising_handle_ != -1) {
+              enabled_sets.push_back(enabled_sets_[i]);
+            }
+          }
+          if (enabled_sets.size() != 0) {
+            le_advertising_interface_->EnqueueCommand(
+                hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::ENABLED, enabled_sets),
+                module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>));
+          }
+        } break;
+      }
+    }
+    le_address_manager_->AckResume(this);
   }
 
   common::Callback<void(Address, AddressType)> scan_callback_;
@@ -298,6 +457,9 @@
   hci::Controller* controller_;
   hci::LeAdvertisingInterface* le_advertising_interface_;
   std::map<AdvertiserId, Advertiser> advertising_sets_;
+  hci::LeAddressManager* le_address_manager_;
+  bool address_manager_registered = false;
+  bool paused = false;
 
   std::mutex id_mutex_;
   size_t num_instances_;
@@ -323,10 +485,12 @@
 void LeAdvertisingManager::ListDependencies(ModuleList* list) {
   list->add<hci::HciLayer>();
   list->add<hci::Controller>();
+  list->add<hci::AclManager>();
 }
 
 void LeAdvertisingManager::Start() {
-  pimpl_->start(GetHandler(), GetDependency<hci::HciLayer>(), GetDependency<hci::Controller>());
+  pimpl_->start(GetHandler(), GetDependency<hci::HciLayer>(), GetDependency<hci::Controller>(),
+                GetDependency<AclManager>());
 }
 
 void LeAdvertisingManager::Stop() {
@@ -350,8 +514,8 @@
       LOG_WARN("Peer address can not be empty");
       return kInvalidId;
     }
-    if (config.event_type == hci::AdvertisingEventType::ADV_DIRECT_IND ||
-        config.event_type == hci::AdvertisingEventType::ADV_DIRECT_IND_LOW) {
+    if (config.event_type == hci::AdvertisingType::ADV_DIRECT_IND ||
+        config.event_type == hci::AdvertisingType::ADV_DIRECT_IND_LOW) {
       LOG_WARN("Peer address can not be empty for directed advertising");
       return kInvalidId;
     }
@@ -406,4 +570,4 @@
 }
 
 }  // namespace hci
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/hci/le_advertising_manager.h b/gd/hci/le_advertising_manager.h
index 0636288..08f8ba3 100644
--- a/gd/hci/le_advertising_manager.h
+++ b/gd/hci/le_advertising_manager.h
@@ -27,10 +27,9 @@
  public:
   std::vector<GapData> advertisement;
   std::vector<GapData> scan_response;
-  Address random_address;
   uint16_t interval_min;
   uint16_t interval_max;
-  AdvertisingEventType event_type;
+  AdvertisingType event_type;
   AddressType address_type;
   PeerAddressType peer_address_type;
   Address peer_address;
@@ -41,21 +40,23 @@
 
 class ExtendedAdvertisingConfig : public AdvertisingConfig {
  public:
-  bool connectable;
-  bool scannable;
-  bool directed;
-  bool high_duty_directed_connectable;
-  bool legacy_pdus;
-  bool anonymous;
-  bool include_tx_power;
+  bool connectable = false;
+  bool scannable = false;
+  bool directed = false;
+  bool high_duty_directed_connectable = false;
+  bool legacy_pdus = false;
+  bool anonymous = false;
+  bool include_tx_power = false;
   bool use_le_coded_phy;       // Primary advertisement PHY is LE Coded
   uint8_t secondary_max_skip;  // maximum advertising events to be skipped, 0x0 send AUX_ADV_IND prior ot the next event
   SecondaryPhyType secondary_advertising_phy;
-  uint8_t sid;
-  Enable enable_scan_request_notifications;
+  uint8_t sid = 0x00;
+  Enable enable_scan_request_notifications = Enable::DISABLED;
   OwnAddressType own_address_type;
-  Operation operation;
-  FragmentPreference fragment_preference;
+  Operation operation;  // TODO(b/149221472): Support fragmentation
+  FragmentPreference fragment_preference = FragmentPreference::CONTROLLER_SHOULD_NOT;
+  ExtendedAdvertisingConfig() = default;
+  ExtendedAdvertisingConfig(const AdvertisingConfig& config);
 };
 
 using AdvertiserId = int32_t;
diff --git a/gd/hci/le_advertising_manager_test.cc b/gd/hci/le_advertising_manager_test.cc
index d2a5b7b..5ae7ffd 100644
--- a/gd/hci/le_advertising_manager_test.cc
+++ b/gd/hci/le_advertising_manager_test.cc
@@ -25,6 +25,7 @@
 #include <gtest/gtest.h>
 
 #include "common/bind.h"
+#include "hci/acl_manager.h"
 #include "hci/address.h"
 #include "hci/controller.h"
 #include "hci/hci_layer.h"
@@ -74,17 +75,10 @@
 
 class TestHciLayer : public HciLayer {
  public:
-  TestHciLayer() {
-    RegisterEventHandler(EventCode::COMMAND_COMPLETE,
-                         base::Bind(&TestHciLayer::CommandCompleteCallback, common::Unretained(this)), nullptr);
-    RegisterEventHandler(EventCode::COMMAND_STATUS,
-                         base::Bind(&TestHciLayer::CommandStatusCallback, common::Unretained(this)), nullptr);
-  }
-
   void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+                      common::ContextualOnceCallback<void(CommandStatusView)> on_status) override {
     auto packet_view = CommandPacketView::Create(GetPacketView(std::move(command)));
-    ASSERT(packet_view.IsValid());
+    ASSERT_TRUE(packet_view.IsValid());
     command_queue_.push_back(packet_view);
     command_status_callbacks.push_back(std::move(on_status));
     if (command_promise_ != nullptr &&
@@ -98,16 +92,16 @@
   }
 
   void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+                      common::ContextualOnceCallback<void(CommandCompleteView)> on_complete) override {
     auto packet_view = CommandPacketView::Create(GetPacketView(std::move(command)));
-    ASSERT(packet_view.IsValid());
+    ASSERT_TRUE(packet_view.IsValid());
     command_queue_.push_back(packet_view);
     command_complete_callbacks.push_back(std::move(on_complete));
     if (command_promise_ != nullptr &&
         (command_op_code_ == OpCode::NONE || command_op_code_ == packet_view.GetOpCode())) {
       if (command_op_code_ == OpCode::LE_MULTI_ADVT) {
         auto sub_view = LeMultiAdvtView::Create(LeAdvertisingCommandView::Create(packet_view));
-        ASSERT(sub_view.IsValid());
+        ASSERT_TRUE(sub_view.IsValid());
         if (sub_view.GetSubCmd() != command_sub_ocf_) {
           return;
         }
@@ -133,23 +127,26 @@
   }
 
   ConnectionManagementCommandView GetCommandPacket(OpCode op_code) {
-    ASSERT(!command_queue_.empty());
+    if (command_queue_.empty()) {
+      return ConnectionManagementCommandView::Create(
+          CommandPacketView::Create(std::make_shared<std::vector<uint8_t>>()));
+    }
     CommandPacketView command_packet_view = CommandPacketView::Create(command_queue_.front());
     command_queue_.pop_front();
     ConnectionManagementCommandView command = ConnectionManagementCommandView::Create(command_packet_view);
-    ASSERT(command.IsValid());
+    EXPECT_TRUE(command.IsValid());
     EXPECT_EQ(command.GetOpCode(), op_code);
 
     return command;
   }
 
-  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
-                            os::Handler* handler) override {
+  void RegisterEventHandler(EventCode event_code,
+                            common::ContextualCallback<void(EventPacketView)> event_handler) override {
     registered_events_[event_code] = event_handler;
   }
 
-  void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
-                              os::Handler* handler) override {
+  void RegisterLeEventHandler(SubeventCode subevent_code,
+                              common::ContextualCallback<void(LeMetaEventView)> event_handler) override {
     registered_le_events_[subevent_code] = event_handler;
   }
 
@@ -158,8 +155,8 @@
     EventPacketView event = EventPacketView::Create(packet);
     ASSERT_TRUE(event.IsValid());
     EventCode event_code = event.GetEventCode();
-    ASSERT_TRUE(registered_events_.find(event_code) != registered_events_.end()) << EventCodeText(event_code);
-    registered_events_[event_code].Run(event);
+    ASSERT_NE(registered_events_.find(event_code), registered_events_.end()) << EventCodeText(event_code);
+    registered_events_[event_code].Invoke(event);
   }
 
   void IncomingLeMetaEvent(std::unique_ptr<LeMetaEventBuilder> event_builder) {
@@ -168,34 +165,38 @@
     LeMetaEventView meta_event_view = LeMetaEventView::Create(event);
     ASSERT_TRUE(meta_event_view.IsValid());
     SubeventCode subevent_code = meta_event_view.GetSubeventCode();
-    ASSERT_TRUE(registered_le_events_.find(subevent_code) != registered_le_events_.end())
+    ASSERT_NE(registered_le_events_.find(subevent_code), registered_le_events_.end())
         << SubeventCodeText(subevent_code);
-    registered_le_events_[subevent_code].Run(meta_event_view);
+    registered_le_events_[subevent_code].Invoke(meta_event_view);
   }
 
   void CommandCompleteCallback(EventPacketView event) {
     CommandCompleteView complete_view = CommandCompleteView::Create(event);
-    ASSERT(complete_view.IsValid());
-    std::move(command_complete_callbacks.front()).Run(complete_view);
+    ASSERT_TRUE(complete_view.IsValid());
+    std::move(command_complete_callbacks.front()).Invoke(complete_view);
     command_complete_callbacks.pop_front();
   }
 
   void CommandStatusCallback(EventPacketView event) {
     CommandStatusView status_view = CommandStatusView::Create(event);
-    ASSERT(status_view.IsValid());
-    std::move(command_status_callbacks.front()).Run(status_view);
+    ASSERT_TRUE(status_view.IsValid());
+    std::move(command_status_callbacks.front()).Invoke(status_view);
     command_status_callbacks.pop_front();
   }
 
   void ListDependencies(ModuleList* list) override {}
-  void Start() override {}
+  void Start() override {
+    RegisterEventHandler(EventCode::COMMAND_COMPLETE,
+                         GetHandler()->BindOn(this, &TestHciLayer::CommandCompleteCallback));
+    RegisterEventHandler(EventCode::COMMAND_STATUS, GetHandler()->BindOn(this, &TestHciLayer::CommandStatusCallback));
+  }
   void Stop() override {}
 
  private:
-  std::map<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
-  std::map<SubeventCode, common::Callback<void(LeMetaEventView)>> registered_le_events_;
-  std::list<base::OnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
-  std::list<base::OnceCallback<void(CommandStatusView)>> command_status_callbacks;
+  std::map<EventCode, common::ContextualCallback<void(EventPacketView)>> registered_events_;
+  std::map<SubeventCode, common::ContextualCallback<void(LeMetaEventView)>> registered_le_events_;
+  std::list<common::ContextualOnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
+  std::list<common::ContextualOnceCallback<void(CommandStatusView)>> command_status_callbacks;
 
   std::list<CommandPacketView> command_queue_;
   mutable std::mutex mutex_;
@@ -204,20 +205,77 @@
   SubOcf command_sub_ocf_;
 };
 
+class TestLeAddressManager : public LeAddressManager {
+ public:
+  TestLeAddressManager(
+      common::Callback<void(std::unique_ptr<CommandPacketBuilder>)> enqueue_command,
+      os::Handler* handler,
+      Address public_address,
+      uint8_t connect_list_size,
+      uint8_t resolving_list_size)
+      : LeAddressManager(enqueue_command, handler, public_address, connect_list_size, resolving_list_size) {}
+
+  AddressPolicy Register(LeAddressManagerCallback* callback) override {
+    return AddressPolicy::USE_STATIC_ADDRESS;
+  }
+
+  void Unregister(LeAddressManagerCallback* callback) override {}
+
+  AddressWithType GetAnotherAddress() override {
+    hci::Address address;
+    Address::FromString("05:04:03:02:01:00", address);
+    auto random_address = AddressWithType(address, AddressType::RANDOM_DEVICE_ADDRESS);
+    return random_address;
+  }
+};
+
+class TestAclManager : public AclManager {
+ public:
+  LeAddressManager* GetLeAddressManager() override {
+    return test_le_address_manager_;
+  }
+
+ protected:
+  void Start() override {
+    thread_ = new os::Thread("thread", os::Thread::Priority::NORMAL);
+    handler_ = new os::Handler(thread_);
+    Address address({0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
+    test_le_address_manager_ = new TestLeAddressManager(
+        common::Bind(&TestAclManager::enqueue_command, common::Unretained(this)), handler_, address, 0x3F, 0x3F);
+  }
+
+  void Stop() override {
+    delete test_le_address_manager_;
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+
+  void SetRandomAddress(Address address) {}
+
+  void enqueue_command(std::unique_ptr<CommandPacketBuilder> command_packet){};
+
+  os::Thread* thread_;
+  os::Handler* handler_;
+  TestLeAddressManager* test_le_address_manager_;
+};
+
 class LeAdvertisingManagerTest : public ::testing::Test {
  protected:
   void SetUp() override {
     test_hci_layer_ = new TestHciLayer;  // Ownership is transferred to registry
     test_controller_ = new TestController;
+    test_acl_manager_ = new TestAclManager;
     test_controller_->AddSupported(param_opcode_);
     fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
     fake_registry_.InjectTestModule(&Controller::Factory, test_controller_);
+    fake_registry_.InjectTestModule(&AclManager::Factory, test_acl_manager_);
     client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory);
     ASSERT_NE(client_handler_, nullptr);
     test_controller_->num_advertisers = 1;
-    fake_registry_.Start<LeAdvertisingManager>(&thread_);
-    le_advertising_manager_ =
-        static_cast<LeAdvertisingManager*>(fake_registry_.GetModuleUnderTest(&LeAdvertisingManager::Factory));
+    le_advertising_manager_ = fake_registry_.Start<LeAdvertisingManager>(&thread_);
   }
 
   void TearDown() override {
@@ -228,6 +286,7 @@
   TestModuleRegistry fake_registry_;
   TestHciLayer* test_hci_layer_ = nullptr;
   TestController* test_controller_ = nullptr;
+  TestAclManager* test_acl_manager_ = nullptr;
   os::Thread& thread_ = fake_registry_.GetTestThread();
   LeAdvertisingManager* le_advertising_manager_ = nullptr;
   os::Handler* client_handler_ = nullptr;
@@ -295,7 +354,7 @@
 
 TEST_F(LeAdvertisingManagerTest, create_advertiser_test) {
   AdvertisingConfig advertising_config{};
-  advertising_config.event_type = AdvertisingEventType::ADV_IND;
+  advertising_config.event_type = AdvertisingType::ADV_IND;
   advertising_config.address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
   std::vector<GapData> gap_data{};
   GapData data_item{};
@@ -313,8 +372,10 @@
                                                       client_handler_);
   ASSERT_NE(LeAdvertisingManager::kInvalidId, id);
   std::vector<OpCode> adv_opcodes = {
-      OpCode::LE_SET_ADVERTISING_PARAMETERS, OpCode::LE_SET_RANDOM_ADDRESS,     OpCode::LE_SET_SCAN_RESPONSE_DATA,
-      OpCode::LE_SET_ADVERTISING_DATA,       OpCode::LE_SET_ADVERTISING_ENABLE,
+      OpCode::LE_SET_ADVERTISING_PARAMETERS,
+      OpCode::LE_SET_SCAN_RESPONSE_DATA,
+      OpCode::LE_SET_ADVERTISING_DATA,
+      OpCode::LE_SET_ADVERTISING_ENABLE,
   };
   std::vector<uint8_t> success_vector{static_cast<uint8_t>(ErrorCode::SUCCESS)};
   auto result = last_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
@@ -336,7 +397,7 @@
 
 TEST_F(LeAndroidHciAdvertisingManagerTest, create_advertiser_test) {
   AdvertisingConfig advertising_config{};
-  advertising_config.event_type = AdvertisingEventType::ADV_IND;
+  advertising_config.event_type = AdvertisingType::ADV_IND;
   advertising_config.address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
   std::vector<GapData> gap_data{};
   GapData data_item{};
@@ -362,7 +423,7 @@
   for (size_t i = 0; i < sub_ocf.size(); i++) {
     auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_MULTI_ADVT);
     auto sub_packet = LeMultiAdvtView::Create(LeAdvertisingCommandView::Create(packet));
-    ASSERT(sub_packet.IsValid());
+    ASSERT_TRUE(sub_packet.IsValid());
     test_hci_layer_->IncomingEvent(LeMultiAdvtCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS, sub_ocf[i]));
     num_commands -= 1;
   }
@@ -377,7 +438,7 @@
 
 TEST_F(LeExtendedAdvertisingManagerTest, create_advertiser_test) {
   ExtendedAdvertisingConfig advertising_config{};
-  advertising_config.event_type = AdvertisingEventType::ADV_IND;
+  advertising_config.event_type = AdvertisingType::ADV_IND;
   advertising_config.address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
   std::vector<GapData> gap_data{};
   GapData data_item{};
@@ -390,6 +451,7 @@
   advertising_config.advertisement = gap_data;
   advertising_config.scan_response = gap_data;
   advertising_config.channel_map = 1;
+  advertising_config.sid = 0x01;
 
   auto last_command_future = test_hci_layer_->GetCommandFuture(OpCode::LE_SET_EXTENDED_ADVERTISING_ENABLE);
   auto id = le_advertising_manager_->ExtendedCreateAdvertiser(advertising_config, scan_callback,
diff --git a/gd/hci/le_report.h b/gd/hci/le_report.h
index 96f55db..7beeed6 100644
--- a/gd/hci/le_report.h
+++ b/gd/hci/le_report.h
@@ -57,11 +57,14 @@
 class DirectedLeReport : public LeReport {
  public:
   explicit DirectedLeReport(const LeDirectedAdvertisingReport& advertisement)
-      : LeReport(advertisement), direct_address_type_(advertisement.address_type_) {}
+      : LeReport(advertisement), direct_address_type_(advertisement.address_type_),
+        direct_address_(advertisement.direct_address_) {}
   explicit DirectedLeReport(const LeExtendedAdvertisingReport& advertisement)
-      : LeReport(advertisement), direct_address_type_(advertisement.address_type_) {}
+      : LeReport(advertisement), direct_address_type_(advertisement.address_type_),
+        direct_address_(advertisement.direct_address_) {}
 
   const DirectAdvertisingAddressType direct_address_type_{};
+  const Address direct_address_{};
 };
 
 class ExtendedLeReport : public DirectedLeReport {
diff --git a/gd/hci/le_scanning_interface.h b/gd/hci/le_scanning_interface.h
index 97a6766..49d1ab1 100644
--- a/gd/hci/le_scanning_interface.h
+++ b/gd/hci/le_scanning_interface.h
@@ -16,35 +16,22 @@
 
 #pragma once
 
-#include "common/callback.h"
+#include "hci/command_interface.h"
 #include "hci/hci_packets.h"
-#include "os/handler.h"
-#include "os/utils.h"
 
 namespace bluetooth {
 namespace hci {
 
-class LeScanningInterface {
- public:
-  LeScanningInterface() = default;
-  virtual ~LeScanningInterface() = default;
-  DISALLOW_COPY_AND_ASSIGN(LeScanningInterface);
-
-  virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
-
-  virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
-
-  static constexpr hci::SubeventCode LeScanningEvents[] = {
-      hci::SubeventCode::SCAN_TIMEOUT,
-      hci::SubeventCode::ADVERTISING_REPORT,
-      hci::SubeventCode::DIRECTED_ADVERTISING_REPORT,
-      hci::SubeventCode::EXTENDED_ADVERTISING_REPORT,
-      hci::SubeventCode::PERIODIC_ADVERTISING_REPORT,
-      hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED,
-      hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST,
-  };
+constexpr hci::SubeventCode LeScanningEvents[] = {
+    hci::SubeventCode::SCAN_TIMEOUT,
+    hci::SubeventCode::ADVERTISING_REPORT,
+    hci::SubeventCode::DIRECTED_ADVERTISING_REPORT,
+    hci::SubeventCode::EXTENDED_ADVERTISING_REPORT,
+    hci::SubeventCode::PERIODIC_ADVERTISING_REPORT,
+    hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED,
+    hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST,
 };
+
+typedef CommandInterface<LeScanningCommandBuilder> LeScanningInterface;
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/le_scanning_manager.cc b/gd/hci/le_scanning_manager.cc
index c3b96d9..ac86b58 100644
--- a/gd/hci/le_scanning_manager.cc
+++ b/gd/hci/le_scanning_manager.cc
@@ -17,6 +17,7 @@
 #include <mutex>
 #include <set>
 
+#include "hci/acl_manager.h"
 #include "hci/controller.h"
 #include "hci/hci_layer.h"
 #include "hci/hci_packets.h"
@@ -40,15 +41,23 @@
   LE_5_0 = 3,
 };
 
-struct LeScanningManager::impl {
+struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback {
   impl(Module* module) : module_(module), le_scanning_interface_(nullptr) {}
 
-  void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::Controller* controller) {
+  ~impl() {
+    if (address_manager_registered) {
+      le_address_manager_->Unregister(this);
+    }
+  }
+
+  void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::Controller* controller,
+             hci::AclManager* acl_manager) {
     module_handler_ = handler;
     hci_layer_ = hci_layer;
     controller_ = controller;
+    le_address_manager_ = acl_manager->GetLeAddressManager();
     le_scanning_interface_ = hci_layer_->GetLeScanningInterface(
-        common::Bind(&LeScanningManager::impl::handle_scan_results, common::Unretained(this)), module_handler_);
+        module_handler_->BindOn(this, &LeScanningManager::impl::handle_scan_results));
     if (controller_->IsSupported(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS)) {
       api_type_ = ScanApiType::LE_5_0;
     } else if (controller_->IsSupported(OpCode::LE_EXTENDED_SCAN_PARAMS)) {
@@ -122,43 +131,60 @@
       case ScanApiType::LE_5_0:
         le_scanning_interface_->EnqueueCommand(hci::LeSetExtendedScanParametersBuilder::Create(
                                                    own_address_type_, filter_policy_, phys_in_use, parameter_vector),
-                                               common::BindOnce(impl::check_status), module_handler_);
+                                               module_handler_->BindOnce(impl::check_status));
         break;
       case ScanApiType::ANDROID_HCI:
         le_scanning_interface_->EnqueueCommand(
             hci::LeExtendedScanParamsBuilder::Create(LeScanType::ACTIVE, interval_ms_, window_ms_, own_address_type_,
                                                      filter_policy_),
-            common::BindOnce(impl::check_status), module_handler_);
+            module_handler_->BindOnce(impl::check_status));
 
         break;
       case ScanApiType::LE_4_0:
         le_scanning_interface_->EnqueueCommand(
             hci::LeSetScanParametersBuilder::Create(LeScanType::ACTIVE, interval_ms_, window_ms_, own_address_type_,
                                                     filter_policy_),
-            common::BindOnce(impl::check_status), module_handler_);
+            module_handler_->BindOnce(impl::check_status));
         break;
     }
   }
 
   void start_scan(LeScanningManagerCallbacks* le_scanning_manager_callbacks) {
     registered_callback_ = le_scanning_manager_callbacks;
+
+    if (!address_manager_registered) {
+      le_address_manager_->Register(this);
+      address_manager_registered = true;
+    }
+
+    // If we receive start_scan during paused, replace the cached_registered_callback_ for OnResume
+    if (cached_registered_callback_ != nullptr) {
+      cached_registered_callback_ = registered_callback_;
+      return;
+    }
+
     switch (api_type_) {
       case ScanApiType::LE_5_0:
         le_scanning_interface_->EnqueueCommand(
             hci::LeSetExtendedScanEnableBuilder::Create(Enable::ENABLED,
                                                         FilterDuplicates::DISABLED /* filter duplicates */, 0, 0),
-            common::BindOnce(impl::check_status), module_handler_);
+            module_handler_->BindOnce(impl::check_status));
         break;
       case ScanApiType::ANDROID_HCI:
       case ScanApiType::LE_4_0:
         le_scanning_interface_->EnqueueCommand(
             hci::LeSetScanEnableBuilder::Create(Enable::ENABLED, Enable::DISABLED /* filter duplicates */),
-            common::BindOnce(impl::check_status), module_handler_);
+            module_handler_->BindOnce(impl::check_status));
         break;
     }
   }
 
-  void stop_scan(common::Callback<void()> on_stopped) {
+  void stop_scan(common::Callback<void()> on_stopped, bool from_on_pause) {
+    if (address_manager_registered && !from_on_pause) {
+      cached_registered_callback_ = nullptr;
+      le_address_manager_->Unregister(this);
+      address_manager_registered = false;
+    }
     if (registered_callback_ == nullptr) {
       return;
     }
@@ -168,32 +194,53 @@
         le_scanning_interface_->EnqueueCommand(
             hci::LeSetExtendedScanEnableBuilder::Create(Enable::DISABLED,
                                                         FilterDuplicates::DISABLED /* filter duplicates */, 0, 0),
-            common::BindOnce(impl::check_status), module_handler_);
+            module_handler_->BindOnce(impl::check_status));
         registered_callback_ = nullptr;
         break;
       case ScanApiType::ANDROID_HCI:
       case ScanApiType::LE_4_0:
         le_scanning_interface_->EnqueueCommand(
             hci::LeSetScanEnableBuilder::Create(Enable::DISABLED, Enable::DISABLED /* filter duplicates */),
-            common::BindOnce(impl::check_status), module_handler_);
+            module_handler_->BindOnce(impl::check_status));
         registered_callback_ = nullptr;
         break;
     }
   }
 
+  void OnPause() override {
+    cached_registered_callback_ = registered_callback_;
+    stop_scan(common::Bind(&impl::ack_pause, common::Unretained(this)), true);
+  }
+
+  void ack_pause() {
+    le_address_manager_->AckPause(this);
+  }
+
+  void OnResume() override {
+    if (cached_registered_callback_ != nullptr) {
+      auto cached_registered_callback = cached_registered_callback_;
+      cached_registered_callback_ = nullptr;
+      start_scan(cached_registered_callback);
+    }
+    le_address_manager_->AckResume(this);
+  }
+
   ScanApiType api_type_;
 
-  LeScanningManagerCallbacks* registered_callback_;
+  LeScanningManagerCallbacks* registered_callback_ = nullptr;
+  LeScanningManagerCallbacks* cached_registered_callback_ = nullptr;
   Module* module_;
   os::Handler* module_handler_;
   hci::HciLayer* hci_layer_;
   hci::Controller* controller_;
   hci::LeScanningInterface* le_scanning_interface_;
+  hci::LeAddressManager* le_address_manager_;
+  bool address_manager_registered = false;
 
   uint32_t interval_ms_{1000};
   uint16_t window_ms_{1000};
   AddressType own_address_type_{AddressType::PUBLIC_DEVICE_ADDRESS};
-  LeSetScanningFilterPolicy filter_policy_{LeSetScanningFilterPolicy::ACCEPT_ALL};
+  LeScanningFilterPolicy filter_policy_{LeScanningFilterPolicy::ACCEPT_ALL};
 
   static void check_status(CommandCompleteView view) {
     switch (view.GetCommandOpCode()) {
@@ -235,10 +282,12 @@
 void LeScanningManager::ListDependencies(ModuleList* list) {
   list->add<hci::HciLayer>();
   list->add<hci::Controller>();
+  list->add<hci::AclManager>();
 }
 
 void LeScanningManager::Start() {
-  pimpl_->start(GetHandler(), GetDependency<hci::HciLayer>(), GetDependency<hci::Controller>());
+  pimpl_->start(GetHandler(), GetDependency<hci::HciLayer>(), GetDependency<hci::Controller>(),
+                GetDependency<AclManager>());
 }
 
 void LeScanningManager::Stop() {
@@ -254,7 +303,7 @@
 }
 
 void LeScanningManager::StopScan(common::Callback<void()> on_stopped) {
-  GetHandler()->Post(common::Bind(&impl::stop_scan, common::Unretained(pimpl_.get()), on_stopped));
+  GetHandler()->Post(common::Bind(&impl::stop_scan, common::Unretained(pimpl_.get()), on_stopped, false));
 }
 
 }  // namespace hci
diff --git a/gd/hci/le_scanning_manager_test.cc b/gd/hci/le_scanning_manager_test.cc
index fe98159..6d75ed1 100644
--- a/gd/hci/le_scanning_manager_test.cc
+++ b/gd/hci/le_scanning_manager_test.cc
@@ -23,6 +23,7 @@
 #include <map>
 
 #include "common/bind.h"
+#include "hci/acl_manager.h"
 #include "hci/address.h"
 #include "hci/controller.h"
 #include "hci/hci_layer.h"
@@ -67,15 +68,8 @@
 
 class TestHciLayer : public HciLayer {
  public:
-  TestHciLayer() {
-    RegisterEventHandler(EventCode::COMMAND_COMPLETE,
-                         base::Bind(&TestHciLayer::CommandCompleteCallback, common::Unretained(this)), nullptr);
-    RegisterEventHandler(EventCode::COMMAND_STATUS,
-                         base::Bind(&TestHciLayer::CommandStatusCallback, common::Unretained(this)), nullptr);
-  }
-
   void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+                      common::ContextualOnceCallback<void(CommandStatusView)> on_status) override {
     command_queue_.push(std::move(command));
     command_status_callbacks.push_front(std::move(on_status));
     if (command_promise_ != nullptr) {
@@ -85,7 +79,7 @@
   }
 
   void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+                      common::ContextualOnceCallback<void(CommandCompleteView)> on_complete) override {
     command_queue_.push(std::move(command));
     command_complete_callbacks.push_front(std::move(on_complete));
     if (command_promise_ != nullptr) {
@@ -100,30 +94,31 @@
     return command_promise_->get_future();
   }
 
-  std::unique_ptr<CommandPacketBuilder> GetLastCommand() {
-    ASSERT(!command_queue_.empty());
-    auto last = std::move(command_queue_.front());
-    command_queue_.pop();
-    return last;
+  CommandPacketView GetLastCommand() {
+    if (command_queue_.empty()) {
+      return CommandPacketView::Create(GetPacketView(nullptr));
+    } else {
+      auto last = std::move(command_queue_.front());
+      command_queue_.pop();
+      return CommandPacketView::Create(GetPacketView(std::move(last)));
+    }
   }
 
   ConnectionManagementCommandView GetCommandPacket(OpCode op_code) {
-    auto packet_view = GetPacketView(GetLastCommand());
-    CommandPacketView command_packet_view = CommandPacketView::Create(packet_view);
+    CommandPacketView command_packet_view = GetLastCommand();
     ConnectionManagementCommandView command = ConnectionManagementCommandView::Create(command_packet_view);
-    ASSERT(command.IsValid());
+    EXPECT_TRUE(command.IsValid());
     EXPECT_EQ(command.GetOpCode(), op_code);
-
     return command;
   }
 
-  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
-                            os::Handler* handler) override {
+  void RegisterEventHandler(EventCode event_code,
+                            common::ContextualCallback<void(EventPacketView)> event_handler) override {
     registered_events_[event_code] = event_handler;
   }
 
-  void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
-                              os::Handler* handler) override {
+  void RegisterLeEventHandler(SubeventCode subevent_code,
+                              common::ContextualCallback<void(LeMetaEventView)> event_handler) override {
     registered_le_events_[subevent_code] = event_handler;
   }
 
@@ -132,8 +127,8 @@
     EventPacketView event = EventPacketView::Create(packet);
     ASSERT_TRUE(event.IsValid());
     EventCode event_code = event.GetEventCode();
-    ASSERT_TRUE(registered_events_.find(event_code) != registered_events_.end()) << EventCodeText(event_code);
-    registered_events_[event_code].Run(event);
+    ASSERT_NE(registered_events_.find(event_code), registered_events_.end()) << EventCodeText(event_code);
+    registered_events_[event_code].Invoke(event);
   }
 
   void IncomingLeMetaEvent(std::unique_ptr<LeMetaEventBuilder> event_builder) {
@@ -142,48 +137,104 @@
     LeMetaEventView meta_event_view = LeMetaEventView::Create(event);
     ASSERT_TRUE(meta_event_view.IsValid());
     SubeventCode subevent_code = meta_event_view.GetSubeventCode();
-    ASSERT_TRUE(registered_le_events_.find(subevent_code) != registered_le_events_.end())
+    ASSERT_NE(registered_le_events_.find(subevent_code), registered_le_events_.end())
         << SubeventCodeText(subevent_code);
-    registered_le_events_[subevent_code].Run(meta_event_view);
+    registered_le_events_[subevent_code].Invoke(meta_event_view);
   }
 
   void CommandCompleteCallback(EventPacketView event) {
     CommandCompleteView complete_view = CommandCompleteView::Create(event);
-    ASSERT(complete_view.IsValid());
-    std::move(command_complete_callbacks.front()).Run(complete_view);
+    ASSERT_TRUE(complete_view.IsValid());
+    std::move(command_complete_callbacks.front()).Invoke(complete_view);
     command_complete_callbacks.pop_front();
   }
 
   void CommandStatusCallback(EventPacketView event) {
     CommandStatusView status_view = CommandStatusView::Create(event);
-    ASSERT(status_view.IsValid());
-    std::move(command_status_callbacks.front()).Run(status_view);
+    ASSERT_TRUE(status_view.IsValid());
+    std::move(command_status_callbacks.front()).Invoke(status_view);
     command_status_callbacks.pop_front();
   }
 
   void ListDependencies(ModuleList* list) override {}
-  void Start() override {}
+  void Start() override {
+    RegisterEventHandler(EventCode::COMMAND_COMPLETE,
+                         GetHandler()->BindOn(this, &TestHciLayer::CommandCompleteCallback));
+    RegisterEventHandler(EventCode::COMMAND_STATUS, GetHandler()->BindOn(this, &TestHciLayer::CommandStatusCallback));
+  }
   void Stop() override {}
 
  private:
-  std::map<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
-  std::map<SubeventCode, common::Callback<void(LeMetaEventView)>> registered_le_events_;
-  std::list<base::OnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
-  std::list<base::OnceCallback<void(CommandStatusView)>> command_status_callbacks;
+  std::map<EventCode, common::ContextualCallback<void(EventPacketView)>> registered_events_;
+  std::map<SubeventCode, common::ContextualCallback<void(LeMetaEventView)>> registered_le_events_;
+  std::list<common::ContextualOnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
+  std::list<common::ContextualOnceCallback<void(CommandStatusView)>> command_status_callbacks;
 
   std::queue<std::unique_ptr<CommandPacketBuilder>> command_queue_;
   mutable std::mutex mutex_;
   std::unique_ptr<std::promise<void>> command_promise_{};
 };
 
+class TestLeAddressManager : public LeAddressManager {
+ public:
+  TestLeAddressManager(
+      common::Callback<void(std::unique_ptr<CommandPacketBuilder>)> enqueue_command,
+      os::Handler* handler,
+      Address public_address,
+      uint8_t connect_list_size,
+      uint8_t resolving_list_size)
+      : LeAddressManager(enqueue_command, handler, public_address, connect_list_size, resolving_list_size) {}
+
+  AddressPolicy Register(LeAddressManagerCallback* callback) override {
+    return AddressPolicy::USE_STATIC_ADDRESS;
+  }
+
+  void Unregister(LeAddressManagerCallback* callback) override {}
+};
+
+class TestAclManager : public AclManager {
+ public:
+  LeAddressManager* GetLeAddressManager() override {
+    return test_le_address_manager_;
+  }
+
+ protected:
+  void Start() override {
+    thread_ = new os::Thread("thread", os::Thread::Priority::NORMAL);
+    handler_ = new os::Handler(thread_);
+    Address address({0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
+    test_le_address_manager_ = new TestLeAddressManager(
+        common::Bind(&TestAclManager::enqueue_command, common::Unretained(this)), handler_, address, 0x3F, 0x3F);
+  }
+
+  void Stop() override {
+    delete test_le_address_manager_;
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+
+  void SetRandomAddress(Address address) {}
+
+  void enqueue_command(std::unique_ptr<CommandPacketBuilder> command_packet){};
+
+  os::Thread* thread_;
+  os::Handler* handler_;
+  TestLeAddressManager* test_le_address_manager_;
+};
+
 class LeScanningManagerTest : public ::testing::Test {
  protected:
   void SetUp() override {
     test_hci_layer_ = new TestHciLayer;  // Ownership is transferred to registry
     test_controller_ = new TestController;
     test_controller_->AddSupported(param_opcode_);
+    test_acl_manager_ = new TestAclManager;
     fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
     fake_registry_.InjectTestModule(&Controller::Factory, test_controller_);
+    fake_registry_.InjectTestModule(&AclManager::Factory, test_acl_manager_);
     client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory);
     ASSERT_NE(client_handler_, nullptr);
     mock_callbacks_.handler_ = client_handler_;
@@ -209,6 +260,7 @@
   TestModuleRegistry fake_registry_;
   TestHciLayer* test_hci_layer_ = nullptr;
   TestController* test_controller_ = nullptr;
+  TestAclManager* test_acl_manager_ = nullptr;
   os::Thread& thread_ = fake_registry_.GetTestThread();
   LeScanningManager* le_scanning_manager = nullptr;
   os::Handler* client_handler_ = nullptr;
diff --git a/gd/hci/le_security_interface.h b/gd/hci/le_security_interface.h
index 13c3ef3..da1e62d 100644
--- a/gd/hci/le_security_interface.h
+++ b/gd/hci/le_security_interface.h
@@ -16,31 +16,19 @@
 
 #pragma once
 
-#include "common/callback.h"
+#include "hci/command_interface.h"
 #include "hci/hci_packets.h"
-#include "os/handler.h"
-#include "os/utils.h"
 
 namespace bluetooth {
 namespace hci {
 
-class LeSecurityInterface {
- public:
-  LeSecurityInterface() = default;
-  virtual ~LeSecurityInterface() = default;
-  DISALLOW_COPY_AND_ASSIGN(LeSecurityInterface);
-
-  virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
-
-  virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
-
-  static constexpr hci::SubeventCode LeSecurityEvents[] = {
-      hci::SubeventCode::LONG_TERM_KEY_REQUEST,
-      hci::SubeventCode::READ_LOCAL_P256_PUBLIC_KEY_COMPLETE,
-      hci::SubeventCode::GENERATE_DHKEY_COMPLETE,
-  };
+constexpr hci::SubeventCode LeSecurityEvents[] = {
+    hci::SubeventCode::LONG_TERM_KEY_REQUEST,
+    hci::SubeventCode::READ_LOCAL_P256_PUBLIC_KEY_COMPLETE,
+    hci::SubeventCode::GENERATE_DHKEY_COMPLETE,
 };
+
+typedef CommandInterface<LeSecurityCommandBuilder> LeSecurityInterface;
+
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/security_interface.h b/gd/hci/security_interface.h
index ea15aa0..eea9eec 100644
--- a/gd/hci/security_interface.h
+++ b/gd/hci/security_interface.h
@@ -16,35 +16,33 @@
 
 #pragma once
 
-#include "common/callback.h"
+#include "hci/command_interface.h"
 #include "hci/hci_packets.h"
-#include "os/utils.h"
 
 namespace bluetooth {
 namespace hci {
 
-class SecurityInterface {
- public:
-  SecurityInterface() = default;
-  virtual ~SecurityInterface() = default;
-  DISALLOW_COPY_AND_ASSIGN(SecurityInterface);
-
-  virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
-                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
-
-  virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
-                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
-
-  static constexpr hci::EventCode SecurityEvents[] = {
-      hci::EventCode::ENCRYPTION_CHANGE,         hci::EventCode::CHANGE_CONNECTION_LINK_KEY_COMPLETE,
-      hci::EventCode::MASTER_LINK_KEY_COMPLETE,  hci::EventCode::RETURN_LINK_KEYS,
-      hci::EventCode::PIN_CODE_REQUEST,          hci::EventCode::LINK_KEY_REQUEST,
-      hci::EventCode::LINK_KEY_NOTIFICATION,     hci::EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE,
-      hci::EventCode::IO_CAPABILITY_REQUEST,     hci::EventCode::IO_CAPABILITY_RESPONSE,
-      hci::EventCode::REMOTE_OOB_DATA_REQUEST,   hci::EventCode::SIMPLE_PAIRING_COMPLETE,
-      hci::EventCode::USER_PASSKEY_NOTIFICATION, hci::EventCode::KEYPRESS_NOTIFICATION,
-      hci::EventCode::USER_CONFIRMATION_REQUEST, hci::EventCode::USER_PASSKEY_REQUEST,
-  };
+constexpr hci::EventCode SecurityEvents[] = {
+    hci::EventCode::ENCRYPTION_CHANGE,
+    hci::EventCode::CHANGE_CONNECTION_LINK_KEY_COMPLETE,
+    hci::EventCode::MASTER_LINK_KEY_COMPLETE,
+    hci::EventCode::RETURN_LINK_KEYS,
+    hci::EventCode::PIN_CODE_REQUEST,
+    hci::EventCode::LINK_KEY_REQUEST,
+    hci::EventCode::LINK_KEY_NOTIFICATION,
+    hci::EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE,
+    hci::EventCode::IO_CAPABILITY_REQUEST,
+    hci::EventCode::IO_CAPABILITY_RESPONSE,
+    hci::EventCode::REMOTE_OOB_DATA_REQUEST,
+    hci::EventCode::SIMPLE_PAIRING_COMPLETE,
+    hci::EventCode::USER_PASSKEY_NOTIFICATION,
+    hci::EventCode::KEYPRESS_NOTIFICATION,
+    hci::EventCode::USER_CONFIRMATION_REQUEST,
+    hci::EventCode::USER_PASSKEY_REQUEST,
+    hci::EventCode::REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION,
 };
+
+typedef CommandInterface<SecurityCommandBuilder> SecurityInterface;
+
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/l2cap/Android.bp b/gd/l2cap/Android.bp
index 687c9b9..d00f5ce 100644
--- a/gd/l2cap/Android.bp
+++ b/gd/l2cap/Android.bp
@@ -24,11 +24,13 @@
         "internal/receiver.cc",
         "internal/scheduler_fifo.cc",
         "internal/sender.cc",
+        "le/dynamic_channel.cc",
         "le/dynamic_channel_manager.cc",
         "le/dynamic_channel_service.cc",
         "le/fixed_channel.cc",
         "le/fixed_channel_manager.cc",
         "le/fixed_channel_service.cc",
+        "le/link_options.cc",
         "le/internal/dynamic_channel_service_manager_impl.cc",
         "le/internal/fixed_channel_impl.cc",
         "le/internal/fixed_channel_service_manager_impl.cc",
@@ -45,6 +47,7 @@
         "classic/internal/dynamic_channel_service_manager_test.cc",
         "classic/internal/fixed_channel_impl_test.cc",
         "classic/internal/fixed_channel_service_manager_test.cc",
+        "classic/internal/link_test.cc",
         "classic/internal/link_manager_test.cc",
         "classic/internal/signalling_manager_test.cc",
         "internal/basic_mode_channel_data_controller_test.cc",
@@ -69,13 +72,7 @@
     name: "BluetoothFacade_l2cap_layer",
     srcs: [
         "classic/facade.cc",
-    ],
-}
-
-filegroup {
-    name: "BluetoothCertSource_l2cap_layer",
-    srcs: [
-        "classic/cert/cert.cc",
+        "le/facade.cc",
     ],
 }
 
diff --git a/gd/l2cap/cid.h b/gd/l2cap/cid.h
index 729272c..9a21abb 100644
--- a/gd/l2cap/cid.h
+++ b/gd/l2cap/cid.h
@@ -36,7 +36,5 @@
 constexpr Cid kSmpCid = 6;
 constexpr Cid kSmpBrCid = 7;
 
-constexpr Cid kClassicPairingTriggerCid = kLastFixedChannel - 1;
-
 }  // namespace l2cap
 }  // namespace bluetooth
diff --git a/gd/l2cap/classic/cert/api.proto b/gd/l2cap/classic/cert/api.proto
deleted file mode 100644
index 99256f9..0000000
--- a/gd/l2cap/classic/cert/api.proto
+++ /dev/null
@@ -1,205 +0,0 @@
-syntax = "proto3";
-
-package bluetooth.l2cap.classic.cert;
-
-import "google/protobuf/empty.proto";
-import "facade/common.proto";
-
-service L2capClassicModuleCert {
-  rpc SendL2capPacket(L2capPacket) returns (google.protobuf.Empty) {}
-  rpc SendIFrame(IFrame) returns (SendIFrameResult) {}
-  rpc SendSFrame(SFrame) returns (SendSFrameResult) {}
-
-  rpc SetupLink(SetupLinkRequest) returns (SetupLinkResponse) {}
-  rpc DisconnectLink(DisconnectLinkRequest) returns (google.protobuf.Empty) {}
-
-  rpc SendConnectionRequest(ConnectionRequest) returns (google.protobuf.Empty) {}
-  rpc SendConnectionResponse(ConnectionResponse) returns (SendConnectionResponseResult) {}
-
-  rpc SendConfigurationRequest(ConfigurationRequest) returns (SendConfigurationRequestResult) {}
-  rpc SendConfigurationResponse(ConfigurationResponse) returns (SendConfigurationResponseResult) {}
-
-  rpc SendDisconnectionRequest(DisconnectionRequest) returns (google.protobuf.Empty) {}
-  rpc SendDisconnectionResponse(DisconnectionResponse) returns (SendDisconnectionResponseResult) {}
-
-  rpc SendInformationRequest(InformationRequest) returns (SendInformationRequestResult) {}
-  rpc SendInformationResponse(InformationResponse) returns (SendInformationResponseResult) {}
-
-  rpc FetchL2capLog(google.protobuf.Empty) returns (stream FetchL2capLogResponse) {}
-}
-
-message L2capPacket {
-  facade.BluetoothAddress remote = 1;
-  uint32 channel = 2;
-  bytes payload = 3;
-}
-
-message IFrame {
-  facade.BluetoothAddress remote = 1;
-  uint32 channel = 2;
-  uint32 sar = 3;
-  uint32 tx_seq = 4;
-  uint32 req_seq = 5;
-  uint32 f = 6;
-  uint32 sdu_size = 7;
-  bytes information = 8;
-}
-
-message SendIFrameResult {}
-
-message SFrame {
-  facade.BluetoothAddress remote = 1;
-  uint32 channel = 2;
-  uint32 req_seq = 3;
-  uint32 f = 4;
-  uint32 p = 5;
-  uint32 s = 6;
-}
-
-message SendSFrameResult {}
-
-message DisconnectLinkRequest {
-  facade.BluetoothAddress remote = 1;
-}
-
-message SetupLinkRequest {
-  facade.BluetoothAddress remote = 1;
-}
-
-message SetupLinkResponse {}
-
-message ConnectionRequest {
-  facade.BluetoothAddress remote = 1;
-  uint32 psm = 2;
-  uint32 scid = 3;
-  uint32 signal_id = 4;
-}
-
-message ConnectionResponse {
-  facade.BluetoothAddress remote = 1;
-  uint32 dcid = 2;
-  uint32 scid = 3;
-  uint32 signal_id = 4;
-}
-
-message SendConnectionResponseResult {}
-
-enum ChannelRetransmissionFlowControlMode {
-  BASIC = 0;
-  ERTM = 3;
-  STREAM = 4;
-}
-
-message ChannelRetransmissionFlowControlConfig {
-  ChannelRetransmissionFlowControlMode mode = 1;
-  uint32 tx_window = 2;
-  uint32 max_transmit = 3;
-  uint32 retransmit_timeout = 4;
-  uint32 monitor_timeout = 5;
-  uint32 mps = 6;
-}
-
-message ConfigurationRequest {
-  uint32 dcid = 1;
-  uint32 signal_id = 2;
-  uint32 mtu = 3;
-  ChannelRetransmissionFlowControlConfig retransmission_config = 4;
-  bool fcs = 5;
-}
-
-message SendConfigurationRequestResult {}
-
-message ConfigurationResponse {
-  uint32 scid = 1;
-  uint32 signal_id = 2;
-  uint32 mtu = 3;
-  ChannelRetransmissionFlowControlConfig retransmission_config = 4;
-  bool fcs = 5;
-}
-
-message SendConfigurationResponseResult {}
-
-message DisconnectionRequest {
-  facade.BluetoothAddress remote = 1;
-  uint32 dcid = 2;
-  uint32 scid = 3;
-  uint32 signal_id = 4;
-}
-
-message DisconnectionResponse {
-  facade.BluetoothAddress remote = 1;
-  uint32 dcid = 2;
-  uint32 scid = 3;
-  uint32 signal_id = 4;
-}
-
-message SendDisconnectionResponseResult {}
-
-enum InformationRequestType {
-  CONNECTIONLESS_MTU = 0;
-  EXTENDED_FEATURES = 1;
-  FIXED_CHANNELS = 2;
-}
-
-message InformationRequest {
-  InformationRequestType type = 1;
-  uint32 signal_id = 4;
-}
-
-message SendInformationRequestResult {}
-
-message InformationResponse {
-  InformationRequestType type = 1;
-  uint32 data = 2;
-  uint32 signal_id = 3;
-  uint32 information_value = 4;
-}
-
-message SendInformationResponseResult {}
-
-message FetchL2capLogRequest {}
-
-message CommandReject {
-  uint32 signal_id = 1;
-  uint32 reason = 2;
-}
-
-message EchoRequest {
-  uint32 signal_id = 1;
-  string data = 2;
-}
-message EchoResponse {
-  uint32 signal_id = 1;
-  string data = 2;
-}
-
-message LinkUp {
-  facade.BluetoothAddress remote = 1;
-}
-
-message LinkDown {
-  facade.BluetoothAddress remote = 1;
-}
-
-message FetchL2capLogResponse {
-  oneof response {
-    L2capPacket data_packet = 1;
-    CommandReject command_reject = 2;
-    ConnectionRequest connection_request = 3;
-    ConnectionResponse connection_response = 4;
-    ConfigurationRequest configuration_request = 5;
-    ConfigurationResponse configuration_response = 6;
-    DisconnectionRequest disconnection_request = 7;
-    DisconnectionResponse disconnection_response = 8;
-    EchoRequest echo_request = 9;
-    EchoResponse echo_response = 10;
-    InformationRequest information_request = 11;
-    InformationResponse information_response = 12;
-    LinkUp link_up = 20;
-    LinkDown link_down = 21;
-  }
-}
-
-message StopFetchingL2capLogRequest {}
-
-message StopFetchingL2capLogResponse {}
diff --git a/gd/l2cap/classic/cert/cert.cc b/gd/l2cap/classic/cert/cert.cc
deleted file mode 100644
index d013efa..0000000
--- a/gd/l2cap/classic/cert/cert.cc
+++ /dev/null
@@ -1,545 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <condition_variable>
-#include <cstdint>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <unordered_map>
-
-#include "common/blocking_queue.h"
-#include "grpc/grpc_event_queue.h"
-#include "hci/acl_manager.h"
-#include "hci/cert/cert.h"
-#include "hci/hci_packets.h"
-#include "l2cap/classic/cert/api.grpc.pb.h"
-#include "l2cap/classic/cert/cert.h"
-#include "l2cap/classic/l2cap_classic_module.h"
-#include "l2cap/l2cap_packets.h"
-#include "os/log.h"
-#include "packet/raw_builder.h"
-
-using ::grpc::ServerAsyncResponseWriter;
-using ::grpc::ServerAsyncWriter;
-using ::grpc::ServerContext;
-
-using ::bluetooth::packet::RawBuilder;
-using ::bluetooth::l2cap::classic::cert::L2capPacket;
-
-namespace bluetooth {
-namespace l2cap {
-namespace classic {
-namespace cert {
-
-using namespace facade;
-
-class L2capClassicModuleCertService : public L2capClassicModuleCert::Service {
- public:
-  L2capClassicModuleCertService(hci::AclManager* acl_manager, os::Handler* facade_handler)
-      : handler_(facade_handler), acl_manager_(acl_manager) {
-    ASSERT(handler_ != nullptr);
-    acl_manager_->RegisterCallbacks(&acl_callbacks, handler_);
-  }
-
-  ::grpc::Status SetupLink(::grpc::ServerContext* context,
-                           const ::bluetooth::l2cap::classic::cert::SetupLinkRequest* request,
-                           ::bluetooth::l2cap::classic::cert::SetupLinkResponse* response) override {
-    hci::Address address;
-    hci::Address::FromString(request->remote().address(), address);
-    LOG_INFO("%s", address.ToString().c_str());
-    acl_manager_->CreateConnection(address);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendL2capPacket(::grpc::ServerContext* context, const L2capPacket* request,
-                                 ::google::protobuf::Empty* response) override {
-    std::unique_ptr<RawBuilder> packet = std::make_unique<RawBuilder>();
-    auto req_string = request->payload();
-    packet->AddOctets(std::vector<uint8_t>(req_string.begin(), req_string.end()));
-    std::unique_ptr<BasicFrameBuilder> l2cap_builder = BasicFrameBuilder::Create(request->channel(), std::move(packet));
-    outgoing_packet_queue_.push(std::move(l2cap_builder));
-    send_packet_from_queue();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendIFrame(::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::IFrame* request,
-                            ::bluetooth::l2cap::classic::cert::SendIFrameResult* response) override {
-    std::unique_ptr<RawBuilder> packet = std::make_unique<RawBuilder>();
-    auto req_string = request->information();
-    packet->AddOctets(std::vector<uint8_t>(req_string.begin(), req_string.end()));
-    std::unique_ptr<BasePacketBuilder> l2cap_builder;
-    auto f = static_cast<Final>(request->f());
-    if (request->sar() == static_cast<int>(SegmentationAndReassembly::START)) {
-      l2cap_builder = EnhancedInformationStartFrameBuilder::Create(
-          request->channel(), request->tx_seq(), f, request->req_seq(), request->sdu_size(), std::move(packet));
-    } else {
-      l2cap_builder = EnhancedInformationFrameBuilder::Create(
-          request->channel(), request->tx_seq(), f, request->req_seq(),
-          static_cast<SegmentationAndReassembly>(request->sar()), std::move(packet));
-    }
-    outgoing_packet_queue_.push(std::move(l2cap_builder));
-    send_packet_from_queue();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendSFrame(::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::SFrame* request,
-                            ::bluetooth::l2cap::classic::cert::SendSFrameResult* response) override {
-    auto f = static_cast<Final>(request->f());
-    auto p = static_cast<Poll>(request->p());
-    auto s = static_cast<SupervisoryFunction>(request->s());
-    auto builder = EnhancedSupervisoryFrameBuilder::Create(request->channel(), s, p, f, request->req_seq());
-    outgoing_packet_queue_.push(std::move(builder));
-    send_packet_from_queue();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendConnectionRequest(::grpc::ServerContext* context, const cert::ConnectionRequest* request,
-                                       ::google::protobuf::Empty* response) override {
-    auto builder = ConnectionRequestBuilder::Create(request->signal_id(), request->psm(), request->scid());
-    auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-    outgoing_packet_queue_.push(std::move(l2cap_builder));
-    send_packet_from_queue();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendConnectionResponse(
-      ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::ConnectionResponse* request,
-      ::bluetooth::l2cap::classic::cert::SendConnectionResponseResult* response) override {
-    auto builder = ConnectionResponseBuilder::Create(request->signal_id(), request->dcid(), request->scid(),
-                                                     ConnectionResponseResult::SUCCESS,
-                                                     ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
-    auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-    outgoing_packet_queue_.push(std::move(l2cap_builder));
-    send_packet_from_queue();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendConfigurationRequest(
-      ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::ConfigurationRequest* request,
-      ::bluetooth::l2cap::classic::cert::SendConfigurationRequestResult* response) override {
-    std::vector<std::unique_ptr<ConfigurationOption>> config;
-    if (request->retransmission_config().mode() == ChannelRetransmissionFlowControlMode::ERTM) {
-      auto option = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
-      option->mode_ = RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
-      option->tx_window_size_ = 10;
-      option->max_transmit_ = 20;
-      option->retransmission_time_out_ = 2000;
-      option->monitor_time_out_ = 12000;
-      option->maximum_pdu_size_ = 1010;
-      config.push_back(std::move(option));
-      auto no_fcs = std::make_unique<FrameCheckSequenceOption>();
-      no_fcs->fcs_type_ = FcsType::NO_FCS;
-      config.push_back(std::move(no_fcs));
-    }
-    auto builder = ConfigurationRequestBuilder::Create(request->signal_id(), request->dcid(), Continuation::END,
-                                                       std::move(config));
-    auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-    outgoing_packet_queue_.push(std::move(l2cap_builder));
-    send_packet_from_queue();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendConfigurationResponse(
-      ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::ConfigurationResponse* request,
-      ::bluetooth::l2cap::classic::cert::SendConfigurationResponseResult* response) override {
-    std::vector<std::unique_ptr<ConfigurationOption>> config;
-    if (request->retransmission_config().mode() == ChannelRetransmissionFlowControlMode::ERTM) {
-      auto option = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
-      option->mode_ = RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
-      option->tx_window_size_ = 5;
-      option->max_transmit_ = 1;
-      option->retransmission_time_out_ = 1000;
-      option->monitor_time_out_ = 2000;
-      option->maximum_pdu_size_ = 1010;
-      config.push_back(std::move(option));
-    }
-    auto builder = ConfigurationResponseBuilder::Create(request->signal_id(), request->scid(), Continuation::END,
-                                                        ConfigurationResponseResult::SUCCESS, std::move(config));
-    auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-    outgoing_packet_queue_.push(std::move(l2cap_builder));
-    send_packet_from_queue();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendDisconnectionRequest(::grpc::ServerContext* context, const cert::DisconnectionRequest* request,
-                                          ::google::protobuf::Empty* response) override {
-    auto builder = DisconnectionRequestBuilder::Create(request->signal_id(), request->dcid(), request->scid());
-    auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-    outgoing_packet_queue_.push(std::move(l2cap_builder));
-    send_packet_from_queue();
-
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendDisconnectionResponse(
-      ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::DisconnectionResponse* request,
-      ::bluetooth::l2cap::classic::cert::SendDisconnectionResponseResult* response) override {
-    auto builder = DisconnectionResponseBuilder::Create(request->signal_id(), request->dcid(), request->scid());
-    auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-    outgoing_packet_queue_.push(std::move(l2cap_builder));
-    send_packet_from_queue();
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendInformationRequest(
-      ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::InformationRequest* request,
-      ::bluetooth::l2cap::classic::cert::SendInformationRequestResult* response) override {
-    switch (request->type()) {
-      case InformationRequestType::CONNECTIONLESS_MTU: {
-        auto builder =
-            InformationRequestBuilder::Create(request->signal_id(), InformationRequestInfoType::CONNECTIONLESS_MTU);
-        auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-        outgoing_packet_queue_.push(std::move(l2cap_builder));
-        send_packet_from_queue();
-        break;
-      }
-      case InformationRequestType::EXTENDED_FEATURES: {
-        auto builder = InformationRequestBuilder::Create(request->signal_id(),
-                                                         InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED);
-        auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-        outgoing_packet_queue_.push(std::move(l2cap_builder));
-        send_packet_from_queue();
-        break;
-      }
-      case InformationRequestType::FIXED_CHANNELS: {
-        auto builder = InformationRequestBuilder::Create(request->signal_id(),
-                                                         InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED);
-        auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-        outgoing_packet_queue_.push(std::move(l2cap_builder));
-        send_packet_from_queue();
-        break;
-      }
-      default:
-        break;
-    }
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendInformationResponse(
-      ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::InformationResponse* request,
-      ::bluetooth::l2cap::classic::cert::SendInformationResponseResult* response) override {
-    switch (request->type()) {
-      case InformationRequestType::CONNECTIONLESS_MTU: {
-        auto builder = InformationResponseConnectionlessMtuBuilder::Create(request->signal_id(),
-                                                                           InformationRequestResult::SUCCESS, 100);
-        auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-        outgoing_packet_queue_.push(std::move(l2cap_builder));
-        send_packet_from_queue();
-        break;
-      }
-      case InformationRequestType::EXTENDED_FEATURES: {
-        auto builder = InformationResponseExtendedFeaturesBuilder::Create(
-            request->signal_id(), InformationRequestResult::SUCCESS, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
-        auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-        outgoing_packet_queue_.push(std::move(l2cap_builder));
-        send_packet_from_queue();
-        break;
-      }
-      case InformationRequestType::FIXED_CHANNELS: {
-        constexpr uint64_t kSignallingChannelMask = 0x02;
-        auto builder = InformationResponseFixedChannelsBuilder::Create(
-            request->signal_id(), InformationRequestResult::SUCCESS, kSignallingChannelMask);
-        auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
-        outgoing_packet_queue_.push(std::move(l2cap_builder));
-        send_packet_from_queue();
-        break;
-      }
-      default:
-        break;
-    }
-
-    return ::grpc::Status::OK;
-  }
-
-  std::unique_ptr<packet::BasePacketBuilder> enqueue_packet_to_acl() {
-    auto basic_frame_builder = std::move(outgoing_packet_queue_.front());
-    outgoing_packet_queue_.pop();
-    if (outgoing_packet_queue_.empty()) {
-      acl_connection_->GetAclQueueEnd()->UnregisterEnqueue();
-    }
-    return basic_frame_builder;
-  }
-
-  ::grpc::Status FetchL2capLog(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
-                               ::grpc::ServerWriter<FetchL2capLogResponse>* writer) override {
-    return pending_l2cap_log_.RunLoop(context, writer);
-  }
-
-  void LogEvent(const FetchL2capLogResponse& response) {
-    pending_l2cap_log_.OnIncomingEvent(response);
-  }
-
-  void on_incoming_packet() {
-    auto packet = acl_connection_->GetAclQueueEnd()->TryDequeue();
-    BasicFrameView basic_frame_view = BasicFrameView::Create(*packet);
-    ASSERT(basic_frame_view.IsValid());
-    L2capPacket l2cap_packet;
-    auto payload = basic_frame_view.GetPayload();
-    std::string data = std::string(payload.begin(), payload.end());
-    l2cap_packet.set_payload(data);
-    l2cap_packet.set_channel(basic_frame_view.GetChannelId());
-    if (basic_frame_view.GetChannelId() == kClassicSignallingCid) {
-      ControlView control_view = ControlView::Create(basic_frame_view.GetPayload());
-      ASSERT(control_view.IsValid());
-      handle_signalling_packet(control_view);
-    } else {
-      FetchL2capLogResponse response;
-      response.mutable_data_packet()->set_channel(basic_frame_view.GetChannelId());
-      response.mutable_data_packet()->set_payload(data);
-      LogEvent(response);
-    }
-  }
-
-  void send_packet_from_queue() {
-    if (outgoing_packet_queue_.size() == 1) {
-      acl_connection_->GetAclQueueEnd()->RegisterEnqueue(
-          handler_, common::Bind(&L2capClassicModuleCertService::enqueue_packet_to_acl, common::Unretained(this)));
-    }
-  }
-
-  void handle_signalling_packet(ControlView control_view) {
-    auto code = control_view.GetCode();
-    switch (code) {
-      case CommandCode::COMMAND_REJECT: {
-        CommandRejectView view = CommandRejectView::Create(control_view);
-        ASSERT(view.IsValid());
-        FetchL2capLogResponse response;
-        response.mutable_command_reject()->set_signal_id(control_view.GetIdentifier());
-        LogEvent(response);
-        break;
-      }
-      case CommandCode::CONNECTION_REQUEST: {
-        ConnectionRequestView view = ConnectionRequestView::Create(control_view);
-        ASSERT(view.IsValid());
-        FetchL2capLogResponse response;
-        response.mutable_connection_request()->set_signal_id(control_view.GetIdentifier());
-        response.mutable_connection_request()->set_scid(view.GetSourceCid());
-        response.mutable_connection_request()->set_psm(view.GetPsm());
-        LogEvent(response);
-        break;
-      }
-      case CommandCode::CONNECTION_RESPONSE: {
-        ConnectionResponseView view = ConnectionResponseView::Create(control_view);
-        ASSERT(view.IsValid());
-        FetchL2capLogResponse response;
-        response.mutable_connection_response()->set_signal_id(control_view.GetIdentifier());
-        response.mutable_connection_response()->set_scid(view.GetSourceCid());
-        response.mutable_connection_response()->set_dcid(view.GetDestinationCid());
-        LogEvent(response);
-        break;
-      }
-
-      case CommandCode::CONFIGURATION_REQUEST: {
-        ConfigurationRequestView view = ConfigurationRequestView::Create(control_view);
-        ASSERT(view.IsValid());
-        FetchL2capLogResponse response;
-        response.mutable_configuration_request()->set_signal_id(control_view.GetIdentifier());
-        response.mutable_configuration_request()->set_dcid(view.GetDestinationCid());
-
-        for (auto& option : view.GetConfig()) {
-          if (option->type_ == ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL) {
-            auto config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
-            response.mutable_configuration_request()->mutable_retransmission_config()->set_mode(
-                ChannelRetransmissionFlowControlMode::ERTM);
-            response.mutable_configuration_request()->mutable_retransmission_config()->set_tx_window(
-                config->tx_window_size_);
-            response.mutable_configuration_request()->mutable_retransmission_config()->set_max_transmit(
-                config->max_transmit_);
-            response.mutable_configuration_request()->mutable_retransmission_config()->set_retransmit_timeout(
-                config->retransmission_time_out_);
-            response.mutable_configuration_request()->mutable_retransmission_config()->set_monitor_timeout(
-                config->monitor_time_out_);
-            response.mutable_configuration_request()->mutable_retransmission_config()->set_mps(
-                config->maximum_pdu_size_);
-          }
-        }
-        LogEvent(response);
-        break;
-      }
-      case CommandCode::CONFIGURATION_RESPONSE: {
-        ConfigurationResponseView view = ConfigurationResponseView::Create(control_view);
-        ASSERT(view.IsValid());
-        FetchL2capLogResponse response;
-        response.mutable_configuration_response()->set_signal_id(control_view.GetIdentifier());
-        response.mutable_configuration_response()->set_scid(view.GetSourceCid());
-        LogEvent(response);
-        break;
-      }
-      case CommandCode::DISCONNECTION_REQUEST: {
-        DisconnectionRequestView view = DisconnectionRequestView::Create(control_view);
-        ASSERT(view.IsValid());
-        FetchL2capLogResponse response;
-        response.mutable_disconnection_request()->set_signal_id(control_view.GetIdentifier());
-        response.mutable_disconnection_request()->set_dcid(view.GetDestinationCid());
-        response.mutable_disconnection_request()->set_scid(view.GetSourceCid());
-        LogEvent(response);
-        break;
-      }
-      case CommandCode::DISCONNECTION_RESPONSE: {
-        DisconnectionResponseView view = DisconnectionResponseView::Create(control_view);
-        ASSERT(view.IsValid());
-        FetchL2capLogResponse response;
-        response.mutable_disconnection_response()->set_signal_id(control_view.GetIdentifier());
-        response.mutable_disconnection_response()->set_dcid(view.GetDestinationCid());
-        response.mutable_disconnection_response()->set_scid(view.GetSourceCid());
-        LogEvent(response);
-        break;
-      }
-      case CommandCode::ECHO_RESPONSE: {
-        EchoResponseView view = EchoResponseView::Create(control_view);
-        ASSERT(view.IsValid());
-        FetchL2capLogResponse response;
-        response.mutable_echo_response()->set_signal_id(control_view.GetIdentifier());
-        LogEvent(response);
-        break;
-      }
-      case CommandCode::INFORMATION_REQUEST: {
-        InformationRequestView information_request_view = InformationRequestView::Create(control_view);
-        if (!information_request_view.IsValid()) {
-          return;
-        }
-        FetchL2capLogResponse log_response;
-        log_response.mutable_information_request()->set_signal_id(control_view.GetIdentifier());
-        auto type = information_request_view.GetInfoType();
-        switch (type) {
-          case InformationRequestInfoType::CONNECTIONLESS_MTU: {
-            log_response.mutable_information_request()->set_type(InformationRequestType::CONNECTIONLESS_MTU);
-            break;
-          }
-          case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
-            log_response.mutable_information_request()->set_type(InformationRequestType::EXTENDED_FEATURES);
-            break;
-          }
-          case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: {
-            log_response.mutable_information_request()->set_type(InformationRequestType::FIXED_CHANNELS);
-            break;
-          }
-        }
-        LogEvent(log_response);
-        break;
-      }
-      case CommandCode::INFORMATION_RESPONSE: {
-        InformationResponseView information_response_view = InformationResponseView::Create(control_view);
-        if (!information_response_view.IsValid()) {
-          return;
-        }
-        FetchL2capLogResponse log_response;
-        log_response.mutable_information_response()->set_signal_id(control_view.GetIdentifier());
-        auto type = information_response_view.GetInfoType();
-        switch (type) {
-          case InformationRequestInfoType::CONNECTIONLESS_MTU: {
-            auto view = InformationResponseConnectionlessMtuView::Create(information_response_view);
-            if (!view.IsValid()) {
-              return;
-            }
-            log_response.mutable_information_response()->set_type(InformationRequestType::CONNECTIONLESS_MTU);
-            log_response.mutable_information_response()->set_information_value(view.GetConnectionlessMtu());
-            break;
-          }
-          case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
-            auto view = InformationResponseExtendedFeaturesView::Create(information_response_view);
-            if (!view.IsValid()) {
-              return;
-            }
-            log_response.mutable_information_response()->set_type(InformationRequestType::EXTENDED_FEATURES);
-            int mask = view.GetEnhancedRetransmissionMode() << 3 | view.GetFcsOption() << 5;
-            log_response.mutable_information_response()->set_information_value(mask);
-            break;
-          }
-          case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: {
-            auto view = InformationResponseFixedChannelsView::Create(information_response_view);
-            if (!view.IsValid()) {
-              return;
-            }
-            log_response.mutable_information_response()->set_type(InformationRequestType::FIXED_CHANNELS);
-            log_response.mutable_information_response()->set_information_value(view.GetFixedChannels());
-            break;
-          }
-        }
-        LogEvent(log_response);
-        break;
-      }
-      default:
-        return;
-    }
-  }
-
-  std::queue<std::unique_ptr<BasePacketBuilder>> outgoing_packet_queue_;
-  ::bluetooth::os::Handler* handler_;
-  hci::AclManager* acl_manager_;
-  std::unique_ptr<hci::AclConnection> acl_connection_;
-  ::bluetooth::grpc::GrpcEventQueue<FetchL2capLogResponse> pending_l2cap_log_{"FetchL2capLog"};
-
-  class AclCallbacks : public hci::ConnectionCallbacks {
-   public:
-    AclCallbacks(L2capClassicModuleCertService* module) : module_(module) {}
-    void OnConnectSuccess(std::unique_ptr<hci::AclConnection> connection) override {
-      module_->acl_connection_ = std::move(connection);
-      module_->acl_connection_->RegisterDisconnectCallback(common::BindOnce([](hci::ErrorCode) {}), module_->handler_);
-      module_->acl_connection_->GetAclQueueEnd()->RegisterDequeue(
-          module_->handler_,
-          common::Bind(&L2capClassicModuleCertService::on_incoming_packet, common::Unretained(module_)));
-      dequeue_registered_ = true;
-      FetchL2capLogResponse response;
-      response.mutable_link_up()->mutable_remote()->set_address(module_->acl_connection_->GetAddress().ToString());
-      module_->LogEvent(response);
-    }
-    void OnConnectFail(hci::Address address, hci::ErrorCode reason) override {}
-
-    ~AclCallbacks() {
-      if (dequeue_registered_) {
-        module_->acl_connection_->GetAclQueueEnd()->UnregisterDequeue();
-      }
-    }
-
-    bool dequeue_registered_ = false;
-
-    L2capClassicModuleCertService* module_;
-  } acl_callbacks{this};
-
-  std::mutex mutex_;
-};
-
-void L2capClassicModuleCertModule::ListDependencies(ModuleList* list) {
-  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
-  list->add<hci::AclManager>();
-  list->add<hci::HciLayer>();
-}
-
-void L2capClassicModuleCertModule::Start() {
-  ::bluetooth::grpc::GrpcFacadeModule::Start();
-  GetDependency<hci::HciLayer>()->EnqueueCommand(hci::WriteScanEnableBuilder::Create(hci::ScanEnable::PAGE_SCAN_ONLY),
-                                                 common::BindOnce([](hci::CommandCompleteView) {}), GetHandler());
-  service_ = new L2capClassicModuleCertService(GetDependency<hci::AclManager>(), GetHandler());
-}
-
-void L2capClassicModuleCertModule::Stop() {
-  delete service_;
-  ::bluetooth::grpc::GrpcFacadeModule::Stop();
-}
-
-::grpc::Service* L2capClassicModuleCertModule::GetService() const {
-  return service_;
-}
-
-const ModuleFactory L2capClassicModuleCertModule::Factory =
-    ::bluetooth::ModuleFactory([]() { return new L2capClassicModuleCertModule(); });
-
-}  // namespace cert
-}  // namespace classic
-}  // namespace l2cap
-}  // namespace bluetooth
diff --git a/gd/l2cap/classic/cert/cert.h b/gd/l2cap/classic/cert/cert.h
deleted file mode 100644
index d3108cd..0000000
--- a/gd/l2cap/classic/cert/cert.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include <grpc++/grpc++.h>
-
-#include "grpc/grpc_module.h"
-
-namespace bluetooth {
-namespace l2cap {
-namespace classic {
-namespace cert {
-
-class L2capClassicModuleCertService;
-
-class L2capClassicModuleCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
- public:
-  static const ModuleFactory Factory;
-
-  void ListDependencies(ModuleList* list) override;
-  void Start() override;
-  void Stop() override;
-
-  ::grpc::Service* GetService() const override;
-
- private:
-  L2capClassicModuleCertService* service_;
-};
-
-}  // namespace cert
-}  // namespace classic
-}  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/classic/cert/cert_l2cap.py b/gd/l2cap/classic/cert/cert_l2cap.py
new file mode 100644
index 0000000..853c57d
--- /dev/null
+++ b/gd/l2cap/classic/cert/cert_l2cap.py
@@ -0,0 +1,380 @@
+#!/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 logging
+
+from cert.behavior import IHasBehaviors, SingleArgumentBehavior, ReplyStage
+from cert.closable import Closable
+from cert.closable import safeClose
+from cert.py_acl_manager import PyAclManager
+from cert.truth import assertThat
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import l2cap_packets
+from bluetooth_packets_python3 import RawBuilder
+from bluetooth_packets_python3.l2cap_packets import CommandCode
+from bluetooth_packets_python3.l2cap_packets import Final
+from bluetooth_packets_python3.l2cap_packets import SegmentationAndReassembly
+from bluetooth_packets_python3.l2cap_packets import SupervisoryFunction
+from bluetooth_packets_python3.l2cap_packets import Poll
+from bluetooth_packets_python3.l2cap_packets import InformationRequestInfoType
+from bluetooth_packets_python3.l2cap_packets import ConfigurationResponseResult
+from cert.event_stream import FilteringEventStream
+from cert.event_stream import IEventStream
+from cert.matchers import L2capMatchers
+from cert.captures import L2capCaptures
+
+
+class CertL2capChannel(IEventStream):
+
+    def __init__(self, device, scid, dcid, acl_stream, acl, control_channel, fcs=None):
+        self._device = device
+        self._scid = scid
+        self._dcid = dcid
+        self._acl_stream = acl_stream
+        self._acl = acl
+        self._control_channel = control_channel
+        self._config_rsp_received = False
+        self._config_rsp_sent = False
+        if fcs == l2cap_packets.FcsType.DEFAULT:
+            self._our_acl_view = FilteringEventStream(acl_stream, L2capMatchers.ExtractBasicFrameWithFcs(scid))
+        else:
+            self._our_acl_view = FilteringEventStream(acl_stream, L2capMatchers.ExtractBasicFrame(scid))
+
+    def get_event_queue(self):
+        return self._our_acl_view.get_event_queue()
+
+    def is_configured(self):
+        return self._config_rsp_received and self._config_rsp_sent
+
+    def send(self, packet):
+        frame = l2cap_packets.BasicFrameBuilder(self._dcid, packet)
+        self._acl.send(frame.Serialize())
+
+    def send_i_frame(self,
+                     tx_seq,
+                     req_seq,
+                     f=Final.NOT_SET,
+                     sar=SegmentationAndReassembly.UNSEGMENTED,
+                     payload=None,
+                     fcs=False):
+        if fcs == l2cap_packets.FcsType.DEFAULT:
+            frame = l2cap_packets.EnhancedInformationFrameWithFcsBuilder(self._dcid, tx_seq, f, req_seq, sar, payload)
+        else:
+            frame = l2cap_packets.EnhancedInformationFrameBuilder(self._dcid, tx_seq, f, req_seq, sar, payload)
+        self._acl.send(frame.Serialize())
+
+    def send_s_frame(self, req_seq, s=SupervisoryFunction.RECEIVER_READY, p=Poll.NOT_SET, f=Final.NOT_SET):
+        frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(self._dcid, s, p, f, req_seq)
+        self._acl.send(frame.Serialize())
+
+    def config_request_for_me(self):
+        return L2capMatchers.ConfigurationRequest(self._scid)
+
+    def send_configure_request(self, options, sid=2, continuation=l2cap_packets.Continuation.END):
+        assertThat(self._scid).isNotEqualTo(1)
+        request = l2cap_packets.ConfigurationRequestBuilder(sid, self._dcid, continuation, options)
+        self._control_channel.send(request)
+
+    def _send_information_request(self, type):
+        assertThat(self._scid).isEqualTo(1)
+        signal_id = 3
+        information_request = l2cap_packets.InformationRequestBuilder(signal_id, type)
+        self.send(information_request)
+
+    def send_extended_features_request(self):
+        self._send_information_request(InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED)
+
+    def verify_configuration_request_and_respond(self, result=ConfigurationResponseResult.SUCCESS, options=None):
+        request_capture = L2capCaptures.ConfigurationRequest(self._scid)
+        assertThat(self._control_channel).emits(request_capture)
+        request = request_capture.get()
+        sid = request.GetIdentifier()
+        if options is None:
+            options = []
+        config_response = l2cap_packets.ConfigurationResponseBuilder(sid, self._dcid, l2cap_packets.Continuation.END,
+                                                                     result, options)
+        self._control_channel.send(config_response)
+
+    def send_configuration_response(self, request, result=ConfigurationResponseResult.SUCCESS, options=None):
+        sid = request.GetIdentifier()
+        if options is None:
+            options = []
+        config_response = l2cap_packets.ConfigurationResponseBuilder(sid, self._dcid, l2cap_packets.Continuation.END,
+                                                                     result, options)
+        self._control_channel.send(config_response)
+        self._config_rsp_sent = True
+
+    def verify_configuration_response(self, result=ConfigurationResponseResult.SUCCESS):
+        assertThat(self._control_channel).emits(L2capMatchers.ConfigurationResponse(result))
+
+    def disconnect_and_verify(self):
+        assertThat(self._scid).isNotEqualTo(1)
+        self._control_channel.send(l2cap_packets.DisconnectionRequestBuilder(1, self._dcid, self._scid))
+
+        assertThat(self._control_channel).emits(L2capMatchers.DisconnectionResponse(self._scid, self._dcid))
+
+    def verify_disconnect_request(self):
+        assertThat(self._control_channel).emits(L2capMatchers.DisconnectionRequest(self._dcid, self._scid))
+
+
+class CertL2capControlChannelBehaviors(object):
+
+    def __init__(self, parent):
+        self.on_config_req_behavior = SingleArgumentBehavior(
+            lambda: CertL2capControlChannelBehaviors.CertReplyStage(parent))
+
+    def on_config_req(self, matcher):
+        return self.on_config_req_behavior.begin(matcher)
+
+    class CertReplyStage(ReplyStage):
+
+        def __init__(self, parent):
+            self.parent = parent
+
+        def send_configuration_response(self, result=ConfigurationResponseResult.SUCCESS, options=None):
+            self._commit(lambda request: self._send_configuration_response(request, result, options))
+            return self
+
+        def _send_configuration_response(self, request, result=ConfigurationResponseResult.SUCCESS, options=None):
+            dcid = request.GetDestinationCid()
+            if dcid not in self.parent.scid_to_channel:
+                logging.warning("Received config request with unknown dcid")
+                return
+            self.parent.scid_to_channel[dcid].send_configuration_response(request, result, options)
+
+
+class CertL2cap(Closable, IHasBehaviors):
+
+    def __init__(self, device):
+        self._device = device
+        self._acl_manager = PyAclManager(device)
+        self._acl = None
+
+        self.control_table = {
+            CommandCode.CONNECTION_RESPONSE: self._on_connection_response_default,
+            CommandCode.CONFIGURATION_REQUEST: self._on_configuration_request_default,
+            CommandCode.CONFIGURATION_RESPONSE: self._on_configuration_response_default,
+            CommandCode.DISCONNECTION_REQUEST: self._on_disconnection_request_default,
+            CommandCode.DISCONNECTION_RESPONSE: self._on_disconnection_response_default,
+            CommandCode.INFORMATION_REQUEST: self._on_information_request_default,
+            CommandCode.INFORMATION_RESPONSE: self._on_information_response_default
+        }
+
+        self.scid_to_channel = {}
+
+        self.support_ertm = True
+        self.support_fcs = True
+
+        self._control_behaviors = CertL2capControlChannelBehaviors(self)
+        self._control_behaviors.on_config_req_behavior.set_default(self._send_configuration_response_default)
+
+    def close(self):
+        self._acl_manager.close()
+        safeClose(self._acl)
+
+    def get_behaviors(self):
+        return self._control_behaviors
+
+    def connect_acl(self, remote_addr):
+        self._acl_manager.initiate_connection(remote_addr)
+        self._acl = self._acl_manager.complete_outgoing_connection()
+        self.control_channel = CertL2capChannel(
+            self._device, 1, 1, self._acl.acl_stream, self._acl, control_channel=None)
+        self._acl.acl_stream.register_callback(self._handle_control_packet)
+
+    def open_channel(self, signal_id, psm, scid, fcs=None):
+        self.control_channel.send(l2cap_packets.ConnectionRequestBuilder(signal_id, psm, scid))
+
+        response = L2capCaptures.ConnectionResponse(scid)
+        assertThat(self.control_channel).emits(response)
+        channel = CertL2capChannel(self._device, scid,
+                                   response.get().GetDestinationCid(), self._acl.acl_stream, self._acl,
+                                   self.control_channel, fcs)
+        self.scid_to_channel[scid] = channel
+
+        return channel
+
+    def verify_and_respond_open_channel_from_remote(self, psm=0x33, scid=None, fcs=None):
+
+        request = L2capCaptures.ConnectionRequest(psm)
+        assertThat(self.control_channel).emits(request)
+
+        sid = request.get().GetIdentifier()
+        dcid = request.get().GetSourceCid()
+        if scid is None or scid in self.scid_to_channel:
+            scid = dcid
+        channel = CertL2capChannel(self._device, scid, dcid, self._acl.acl_stream, self._acl, self.control_channel, fcs)
+        self.scid_to_channel[scid] = channel
+
+        connection_response = l2cap_packets.ConnectionResponseBuilder(
+            sid, scid, dcid, l2cap_packets.ConnectionResponseResult.SUCCESS,
+            l2cap_packets.ConnectionResponseStatus.NO_FURTHER_INFORMATION_AVAILABLE)
+        self.control_channel.send(connection_response)
+
+        return channel
+
+    def verify_and_respond_open_channel_from_remote_and_send_config_req(self, psm=0x33):
+        """
+        Verify a connection request, and send a combo packet of connection response and configuration request
+        """
+        request = L2capCaptures.ConnectionRequest(psm)
+        assertThat(self.control_channel).emits(request)
+
+        sid = request.get().GetIdentifier()
+        dcid = request.get().GetSourceCid()
+        scid = dcid
+        channel = CertL2capChannel(self._device, scid, dcid, self._acl.acl_stream, self._acl, self.control_channel)
+        self.scid_to_channel[scid] = channel
+
+        # Connection response and config request combo packet
+        conn_rsp_and_config_req = RawBuilder([
+            0x03, sid, 0x08, 0x00, dcid, 0x00, dcid, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, sid + 1, 0x04, 0x00, dcid,
+            0x00, 0x00, 0x00
+        ])
+        self.control_channel.send(conn_rsp_and_config_req)
+
+        return channel
+
+    # prefer to use channel abstraction instead, if at all possible
+    def send_acl(self, packet):
+        self._acl.send(packet.Serialize())
+
+    def get_control_channel(self):
+        return self.control_channel
+
+    # Disable ERTM when exchange extened feature
+    def claim_ertm_unsupported(self):
+        self.support_ertm = False
+
+    def _on_connection_response_default(self, l2cap_control_view):
+        pass
+
+    def _on_configuration_request_default(self, l2cap_control_view):
+        request = l2cap_packets.ConfigurationRequestView(l2cap_control_view)
+        dcid = request.GetDestinationCid()
+        if dcid not in self.scid_to_channel:
+            logging.warning("Received config request with unknown dcid")
+            return
+        self._control_behaviors.on_config_req_behavior.run(request)
+
+    def _send_configuration_response_default(self, captured_request_view):
+        dcid = captured_request_view.GetDestinationCid()
+        if dcid not in self.scid_to_channel:
+            return
+        self.scid_to_channel[dcid].send_configuration_response(captured_request_view)
+
+    @staticmethod
+    def config_option_basic_explicit(mtu=642):
+        mtu_opt = l2cap_packets.MtuConfigurationOption()
+        mtu_opt.mtu = mtu
+        rfc_opt = l2cap_packets.RetransmissionAndFlowControlConfigurationOption()
+        rfc_opt.mode = l2cap_packets.RetransmissionAndFlowControlModeOption.L2CAP_BASIC
+        return [mtu_opt, rfc_opt]
+
+    @staticmethod
+    def config_option_mtu_explicit(mtu=642):
+        mtu_opt = l2cap_packets.MtuConfigurationOption()
+        mtu_opt.mtu = mtu
+        return [mtu_opt]
+
+    @staticmethod
+    def config_option_ertm(mtu=642,
+                           fcs=None,
+                           max_transmit=10,
+                           mps=1010,
+                           tx_window_size=10,
+                           monitor_time_out=2000,
+                           retransmission_time_out=1000):
+        result = []
+        mtu_opt = l2cap_packets.MtuConfigurationOption()
+        mtu_opt.mtu = mtu
+        result.append(mtu_opt)
+        if fcs is not None:
+            fcs_opt = l2cap_packets.FrameCheckSequenceOption()
+            fcs_opt.fcs_type = fcs
+            result.append(fcs_opt)
+        rfc_opt = l2cap_packets.RetransmissionAndFlowControlConfigurationOption()
+        rfc_opt.mode = l2cap_packets.RetransmissionAndFlowControlModeOption.ENHANCED_RETRANSMISSION
+        rfc_opt.tx_window_size = tx_window_size
+        rfc_opt.max_transmit = max_transmit
+        rfc_opt.retransmission_time_out = retransmission_time_out
+        rfc_opt.monitor_time_out = monitor_time_out
+        rfc_opt.maximum_pdu_size = mps
+        result.append(rfc_opt)
+        return result
+
+    @staticmethod
+    def config_option_ertm_with_max_transmit_one():
+        return CertL2cap.config_option_ertm(max_transmit=1)
+
+    @staticmethod
+    def config_option_ertm_with_mps(mps=1010):
+        return CertL2cap.config_option_ertm(mps=mps)
+
+    def _on_configuration_response_default(self, l2cap_control_view):
+        response = l2cap_packets.ConfigurationResponseView(l2cap_control_view)
+        scid = response.GetSourceCid()
+        if scid not in self.scid_to_channel:
+            logging.warning("Received config request with unknown dcid")
+            return
+        result = response.GetResult()
+        if result == ConfigurationResponseResult.SUCCESS:
+            self.scid_to_channel[scid]._config_rsp_received = True
+
+    def _on_disconnection_request_default(self, l2cap_control_view):
+        disconnection_request = l2cap_packets.DisconnectionRequestView(l2cap_control_view)
+        sid = disconnection_request.GetIdentifier()
+        scid = disconnection_request.GetSourceCid()
+        dcid = disconnection_request.GetDestinationCid()
+        disconnection_response = l2cap_packets.DisconnectionResponseBuilder(sid, dcid, scid)
+        self.control_channel.send(disconnection_response)
+
+    def _on_disconnection_response_default(self, l2cap_control_view):
+        pass
+
+    def _on_information_request_default(self, l2cap_control_view):
+        information_request = l2cap_packets.InformationRequestView(l2cap_control_view)
+        sid = information_request.GetIdentifier()
+        information_type = information_request.GetInfoType()
+        if information_type == l2cap_packets.InformationRequestInfoType.CONNECTIONLESS_MTU:
+            response = l2cap_packets.InformationResponseConnectionlessMtuBuilder(
+                sid, l2cap_packets.InformationRequestResult.SUCCESS, 100)
+            self.control_channel.send(response)
+            return
+        if information_type == l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED:
+            response = l2cap_packets.InformationResponseExtendedFeaturesBuilder(
+                sid, l2cap_packets.InformationRequestResult.SUCCESS, 0, 0, 0, self.support_ertm, 0, self.support_fcs, 0,
+                0, 0, 0)
+            self.control_channel.send(response)
+            return
+        if information_type == l2cap_packets.InformationRequestInfoType.FIXED_CHANNELS_SUPPORTED:
+            response = l2cap_packets.InformationResponseFixedChannelsBuilder(
+                sid, l2cap_packets.InformationRequestResult.SUCCESS, 2)
+            self.control_channel.send(response)
+            return
+
+    def _on_information_response_default(self, l2cap_control_view):
+        pass
+
+    def _handle_control_packet(self, l2cap_packet):
+        packet_bytes = l2cap_packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != 1:
+            return
+        l2cap_control_view = l2cap_packets.ControlView(l2cap_view.GetPayload())
+        fn = self.control_table.get(l2cap_control_view.GetCode())
+        if fn is not None:
+            fn(l2cap_control_view)
diff --git a/gd/l2cap/classic/cert/l2cap_performance_test.py b/gd/l2cap/classic/cert/l2cap_performance_test.py
new file mode 100644
index 0000000..9c387ee
--- /dev/null
+++ b/gd/l2cap/classic/cert/l2cap_performance_test.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 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
+
+
+class L2capPerformanceTest(L2capTestBase):
+
+    def setup_test(self):
+        super().setup_test()
+        self.performance_test_logger = PerformanceTestLogger()
+
+    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()
diff --git a/gd/l2cap/classic/cert/l2cap_test.py b/gd/l2cap/classic/cert/l2cap_test.py
new file mode 100644
index 0000000..672eae1
--- /dev/null
+++ b/gd/l2cap/classic/cert/l2cap_test.py
@@ -0,0 +1,1276 @@
+#
+#   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.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])
+
+
+class L2capTestBase(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(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)
+
+    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()
diff --git a/gd/l2cap/classic/cert/pts_l2cap_test.py b/gd/l2cap/classic/cert/pts_l2cap_test.py
index 2b2fe64..cf8ed4e 100644
--- a/gd/l2cap/classic/cert/pts_l2cap_test.py
+++ b/gd/l2cap/classic/cert/pts_l2cap_test.py
@@ -17,23 +17,22 @@
 import time
 
 from cert.pts_base_test import PTSBaseTestClass
-from cert.event_asserts import EventAsserts
-from cert.event_callback_stream import EventCallbackStream
+from cert.event_stream import EventStream
 from facade import common_pb2
 from facade import rootservice_pb2 as facade_rootservice_pb2
 from l2cap.classic import facade_pb2 as l2cap_facade_pb2
 from google.protobuf import empty_pb2
+from neighbor.facade import facade_pb2 as neighbor_facade
 
 
 class PTSL2capTest(PTSBaseTestClass):
+
     def setup_test(self):
         self.device_under_test = self.gd_devices[0]
 
         self.device_under_test.rootservice.StartStack(
             facade_rootservice_pb2.StartStackRequest(
-                module_under_test=facade_rootservice_pb2.BluetoothModule.Value('L2CAP'),
-            )
-        )
+                module_under_test=facade_rootservice_pb2.BluetoothModule.Value('L2CAP'),))
 
         self.device_under_test.wait_channel_ready()
 
@@ -41,33 +40,321 @@
         pts_address = self.controller_configs.get('pts_address').lower()
         self.device_under_test.address = dut_address
 
-        self.dut_address = common_pb2.BluetoothAddress(
-            address=self.device_under_test.address)
-        self.pts_address = common_pb2.BluetoothAddress(
-            address=str.encode(pts_address))
+        self.dut_address = common_pb2.BluetoothAddress(address=self.device_under_test.address)
+        self.pts_address = common_pb2.BluetoothAddress(address=str.encode(pts_address))
+
+        self.device_under_test.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
 
     def teardown_test(self):
-        self.device_under_test.rootservice.StopStack(
-            facade_rootservice_pb2.StopStackRequest()
-        )
+        self.device_under_test.rootservice.StopStack(facade_rootservice_pb2.StopStackRequest())
 
     def _dut_connection_stream(self):
-        return EventCallbackStream(self.device_under_test.l2cap.FetchConnectionComplete(empty_pb2.Empty()))
+        return EventStream(self.device_under_test.l2cap.FetchConnectionComplete(empty_pb2.Empty()))
 
     def _dut_connection_close_stream(self):
-        return EventCallbackStream(self.device_under_test.l2cap.FetchConnectionClose(empty_pb2.Empty()))
+        return EventStream(self.device_under_test.l2cap.FetchConnectionClose(empty_pb2.Empty()))
 
-    def _assert_connection_complete(self, due_connection_asserts, timeout=30):
-        due_connection_asserts.assert_event_occurs(
-          lambda device : device.remote.address == self.pts_address.address,
-          timeout=timedelta(seconds=timeout)
-        )
+    def _assert_connection_complete(self, dut_connection_stream, timeout=30):
+        dut_connection_stream.assert_event_occurs(
+            lambda device: device.remote.address == self.pts_address.address, timeout=timedelta(seconds=timeout))
 
-    def _assert_connection_close(self, due_connection_close_asserts, timeout=30):
-        due_connection_close_asserts.assert_event_occurs(
-          lambda device : device.remote.address == self.pts_address.address,
-          timeout=timedelta(seconds=timeout)
-        )
+    def _assert_connection_close(self, dut_connection_close_stream, timeout=30):
+        dut_connection_close_stream.assert_event_occurs(
+            lambda device: device.remote.address == self.pts_address.address, timeout=timedelta(seconds=timeout))
+
+    def test_L2CAP_IEX_BV_01_C(self):
+        """
+        L2CAP/COS/IEX/BV-01-C [Query for 1.2 Features]
+        Verify that the IUT transmits an information request command to solicit if the remote device supports
+        Specification 1.2 features.
+        """
+        psm = 1
+        self.device_under_test.l2cap.OpenChannel(l2cap_facade_pb2.OpenChannelRequest(remote=self.pts_address, psm=psm))
+        time.sleep(5)
+
+    def test_L2CAP_IEX_BV_02_C(self):
+        """
+        L2CAP/COS/IEX/BV-02-C [Respond with 1.2 Features]
+        Verify that the IUT responds to an information request command soliciting for Specification 1.2
+        features.
+        """
+        psm = 1
+        retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+        self.device_under_test.l2cap.SetDynamicChannel(
+            l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+        time.sleep(20)
+
+    def test_L2CAP_EXF_BV_01_C(self):
+        """
+        L2CAP/EXF/BV-01-C [Extended Features Information Response for Enhanced Retransmission Mode]
+        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.
+
+        """
+        psm = 1
+        retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+        self.device_under_test.l2cap.SetDynamicChannel(
+            l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+        time.sleep(5)
+
+    def test_L2CAP_EXF_BV_03_C(self):
+        """
+        L2CAP/EXF/BV-03-C [Extended Features Information Response for FCS Option]
+        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.
+        """
+        psm = 1
+        retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+        self.device_under_test.l2cap.SetDynamicChannel(
+            l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+        time.sleep(5)
+
+    def test_L2CAP_CMC_BV_01_C(self):
+        """
+        L2CAP/CMC/BV-01-C [IUT Initiated Configuration of Enhanced Retransmission Mode]
+        Verify the IUT can send a Configuration Request command containing the F&EC option that specifies
+        Enhanced Retransmission Mode.
+        """
+        with self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_CMC_BV_02_C(self):
+        """
+        L2CAP/CMC/BV-02-C [Lower Tester Initiated Configuration of Enhanced Retransmission Mode]
+        Verify the IUT can accept a Configuration Request from the Lower Tester containing an F&EC option
+        that specifies Enhanced Retransmission Mode.
+        """
+        with self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_01_C(self):
+        """
+        L2CAP/ERM/BV-01-C [Transmit I-frames]
+        Verify the IUT can send correctly formatted sequential I-frames with valid values for the enhanced
+        control fields (SAR, F-bit, ReqSeq, TxSeq).
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_02_C(self):
+        """
+        L2CAP/ERM/BV-02-C [Receive I-Frames]
+        Verify the IUT can receive in-sequence valid I-frames and deliver L2CAP SDUs to the Upper Tester
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_03_C(self):
+        """
+        L2CAP/ERM/BV-03-C [Acknowledging Received I-Frames]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_05_C(self):
+        """
+        L2CAP/ERM/BV-05-C [Resume Transmitting I-Frames when an S-Frame [RR] is Received]
+        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.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_06_C(self):
+        """
+        L2CAP/ERM/BV-06-C [Resume Transmitting I-Frames when an I-Frame is Received]
+        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.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_08_C(self):
+        """
+        L2CAP/ERM/BV-08-C [Send S-Frame [RR] with Poll Bit Set]
+        Verify the IUT sends an S-frame [RR] with the Poll bit set when its retransmission timer expires.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_09_C(self):
+        """
+        L2CAP/ERM/BV-09-C [Send S-frame [RR] with Final Bit Set]
+        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.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_10_C(self):
+        """
+      L2CAP/ERM/BV-10-C [Retransmit S-Frame [RR] with Poll Bit Set]
+      Verify the IUT will retransmit the S-frame [RR] with the Poll bit set when the Monitor Timer expires.
+      """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_13_C(self):
+        """
+        L2CAP/ERM/BV-13-C [Respond to S-Frame [REJ]]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_16_C(self):
+        """
+         L2CAP/ERM/BV-16-C [Send S-Frame [REJ]]
+        Verify the IUT can send an S-frame [REJ] after receiving out of sequence I-frames.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(dut_connection_stream)
+            self._assert_connection_close(dut_connection_close_stream, timeout=60)
+
+    def test_L2CAP_ERM_BV_18_C(self):
+        """
+        L2CAP/ERM/BV-18-C [Receive S-Frame [RR] Final Bit = 1]
+        Verify the IUT will retransmit any previously sent I-frames unacknowledged by receipt of an S-Frame
+        [RR] with the Final Bit set.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_19_C(self):
+        """
+        L2CAP/ERM/BV-19-C [Receive I-Frame Final Bit = 1]
+        Verify the IUT will retransmit any previously sent I-frames unacknowledged by receipt of an I-frame
+        with the final bit set.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BV_20_C(self):
+        """
+        L2CAP/ERM/BV-20-C [Enter Remote Busy Condition]
+        Verify the IUT will not retransmit any I-frames when it receives a remote busy indication from the
+        Lower Tester (S-frame [RNR]).
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_COS_CED_BV_01_C(self):
         """
@@ -77,15 +364,13 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
-
-            self.device_under_test.l2cap.OpenChannel(l2cap_facade_pb2.OpenChannelRequest(remote=self.pts_address, psm=psm))
-            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.OpenChannel(
+                l2cap_facade_pb2.OpenChannelRequest(remote=self.pts_address, psm=psm))
+            self._assert_connection_complete(dut_connection_stream)
 
             self.device_under_test.l2cap.CloseChannel(l2cap_facade_pb2.CloseChannelRequest(psm=psm))
-            self._assert_connection_close(due_connection_close_asserts)
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_COS_CED_BV_03_C(self):
         """
@@ -94,16 +379,15 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
 
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
-            self._assert_connection_complete(due_connection_asserts)
-
-            self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'*34))
-            self._assert_connection_close(due_connection_close_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc' * 34))
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_COS_CED_BV_04_C(self):
         """
@@ -112,16 +396,14 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
-
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
-            self._assert_connection_complete(due_connection_asserts)
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
             time.sleep(2)
             self.device_under_test.l2cap.CloseChannel(l2cap_facade_pb2.CloseChannelRequest(psm=psm))
-            self._assert_connection_close(due_connection_close_asserts)
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_COS_CED_BV_05_C(self):
         """
@@ -130,14 +412,12 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
-
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
-            self._assert_connection_complete(due_connection_asserts)
-            self._assert_connection_close(due_connection_close_asserts)
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_COS_CED_BV_07_C(self):
         """
@@ -146,14 +426,12 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
-
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
-            self._assert_connection_complete(due_connection_asserts)
-            self._assert_connection_close(due_connection_close_asserts)
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_COS_CED_BV_08_C(self):
         """
@@ -162,13 +440,10 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
-
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
-
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
             time.sleep(120)
 
     def test_L2CAP_COS_CED_BV_09_C(self):
@@ -178,14 +453,12 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
-
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
-            self._assert_connection_complete(due_connection_asserts)
-            self._assert_connection_close(due_connection_close_asserts)
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_COS_CED_BV_11_C(self):
         """
@@ -194,14 +467,12 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
-
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
-            self._assert_connection_complete(due_connection_asserts)
-            self._assert_connection_close(due_connection_close_asserts)
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_COS_CED_BI_01_C(self):
         """
@@ -210,13 +481,11 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
-
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
-            self._assert_connection_complete(due_connection_asserts)
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
             time.sleep(5)
 
     def test_L2CAP_COS_CFD_BV_03_C(self):
@@ -227,13 +496,11 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
-
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
-            self._assert_connection_close(due_connection_close_asserts)
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_COS_CFD_BV_08_C(self):
         """
@@ -243,15 +510,13 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
             psm = 1
 
-            self.device_under_test.l2cap.OpenChannel(l2cap_facade_pb2.OpenChannelRequest(remote=self.pts_address, psm=psm))
-            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.OpenChannel(
+                l2cap_facade_pb2.OpenChannelRequest(remote=self.pts_address, psm=psm))
+            self._assert_connection_complete(dut_connection_stream)
             self.device_under_test.l2cap.CloseChannel(l2cap_facade_pb2.CloseChannelRequest(psm=psm))
-            self._assert_connection_close(due_connection_close_asserts)
-
+            self._assert_connection_close(dut_connection_close_stream)
 
     def test_L2CAP_ERM_BI_01_C(self):
         """
@@ -261,11 +526,65 @@
         """
         with self._dut_connection_stream() as dut_connection_stream, \
             self._dut_connection_close_stream() as dut_connection_close_stream:
-            due_connection_asserts = EventAsserts(dut_connection_stream)
-            due_connection_close_asserts = EventAsserts(dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(dut_connection_stream)
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BI_03_C(self):
+        """
+        L2CAP/ERM/BI-03-C [Handle Duplicate S-Frame [SREJ]]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
             psm = 1
 
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
-                psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
-            self._assert_connection_complete(due_connection_asserts)
-            self._pending_connection_close(timeout=60)
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BI_04_C(self):
+        """
+        L2CAP/ERM/BI-04-C [Handle Receipt of S-Frame [REJ] and S-Frame [RR, F=1]
+        that Both Require Retransmission of the Same I-Frames]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
+
+    def test_L2CAP_ERM_BI_05_C(self):
+        """
+        L2CAP/ERM/BI-05-C [Handle receipt of S-Frame [REJ] and I-Frame [F=1] that
+        Both Require Retransmission of the Same I-Frames]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(dut_connection_stream)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(dut_connection_close_stream)
diff --git a/gd/l2cap/classic/cert/simple_l2cap_test.py b/gd/l2cap/classic/cert/simple_l2cap_test.py
deleted file mode 100644
index 6756653..0000000
--- a/gd/l2cap/classic/cert/simple_l2cap_test.py
+++ /dev/null
@@ -1,466 +0,0 @@
-#!/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 __future__ import print_function
-
-import os
-import sys
-import time
-sys.path.append(os.environ['ANDROID_BUILD_TOP'] + '/system/bt/gd')
-
-from cert.gd_base_test import GdBaseTestClass
-from cert.event_asserts import EventAsserts
-from cert.event_callback_stream import EventCallbackStream
-from cert import rootservice_pb2 as cert_rootservice_pb2
-from facade import common_pb2
-from facade import rootservice_pb2 as facade_rootservice_pb2
-from google.protobuf import empty_pb2
-from l2cap.classic import facade_pb2 as l2cap_facade_pb2
-from l2cap.classic.cert import api_pb2 as l2cap_cert_pb2
-
-ASYNC_OP_TIME_SECONDS = 1  # TODO: Use events to synchronize events instead
-
-
-def is_connection_request(log):
-    return log.HasField("connection_request")
-
-def is_connection_response(log):
-    return log.HasField("connection_response")
-
-def is_configuration_request(log):
-    return log.HasField("configuration_request")
-
-def is_configuration_response(log):
-    return log.HasField("configuration_response")
-
-def is_disconnection_request(log):
-    return log.HasField("disconnection_request")
-
-def is_disconnection_response(log):
-    return log.HasField("disconnection_response")
-
-def is_echo_response(log):
-    return log.HasField("echo_response")
-
-def is_information_request(log):
-    return log.HasField("information_request")
-
-def is_information_response(log):
-    return log.HasField("information_response")
-
-def is_command_reject(log):
-    return log.HasField("command_reject")
-
-def basic_frame_to_enhanced_information_frame(information_payload):
-    return information_payload[2:]
-
-class SimpleL2capTest(GdBaseTestClass):
-    def setup_test(self):
-        self.device_under_test = self.gd_devices[0]
-        self.cert_device = self.gd_cert_devices[0]
-        self.device_under_test.rootservice.StartStack(
-            facade_rootservice_pb2.StartStackRequest(
-                module_under_test=facade_rootservice_pb2.BluetoothModule.Value('L2CAP'),
-            )
-        )
-        self.cert_device.rootservice.StartStack(
-            cert_rootservice_pb2.StartStackRequest(
-                module_to_test=cert_rootservice_pb2.BluetoothModule.Value('L2CAP'),
-            )
-        )
-
-        self.device_under_test.wait_channel_ready()
-        self.cert_device.wait_channel_ready()
-
-        dut_address = self.device_under_test.controller_read_only_property.ReadLocalAddress(empty_pb2.Empty()).address
-        self.device_under_test.address = dut_address
-        cert_address = self.cert_device.controller_read_only_property.ReadLocalAddress(empty_pb2.Empty()).address
-        self.cert_device.address = cert_address
-
-        self.dut_address = common_pb2.BluetoothAddress(
-            address=self.device_under_test.address)
-        self.cert_address = common_pb2.BluetoothAddress(
-            address=self.cert_device.address)
-
-        self.next_scid = 0x40
-        self.scid_dcid_map = {}
-        self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.BASIC
-
-    def teardown_test(self):
-        self.device_under_test.rootservice.StopStack(
-            facade_rootservice_pb2.StopStackRequest()
-        )
-        self.cert_device.rootservice.StopStack(
-            cert_rootservice_pb2.StopStackRequest()
-        )
-
-    def _register_callbacks(self, event_callback_stream):
-        def handle_connection_request(log):
-            log = log.connection_request
-            self.cert_device.l2cap.SendConnectionResponse(l2cap_cert_pb2.ConnectionResponse(dcid=self.next_scid,scid=log.scid,
-                                                                                            signal_id=log.signal_id))
-            self.scid_dcid_map[self.next_scid] = log.scid
-            self.next_scid += 1
-            self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(
-                dcid=log.scid,
-                signal_id=log.signal_id+1,
-                retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig(
-                    mode=self.retransmission_mode
-                )))
-        self.handle_connection_request = handle_connection_request
-        event_callback_stream.register_callback(self.handle_connection_request, matcher_fn=is_connection_request)
-
-        def handle_connection_response(log):
-            log = log.connection_response
-            self.scid_dcid_map[log.scid] = log.dcid
-            self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(
-                dcid=log.dcid,
-                signal_id=log.signal_id+1,
-                retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig(
-                    mode=self.retransmission_mode
-                )))
-        self.handle_connection_request = handle_connection_request
-        event_callback_stream.register_callback(self.handle_connection_response, matcher_fn=is_connection_response)
-
-        def handle_configuration_request(log):
-            log = log.configuration_request
-            if log.dcid not in self.scid_dcid_map:
-                return
-            dcid = self.scid_dcid_map[log.dcid]
-            self.cert_device.l2cap.SendConfigurationResponse(l2cap_cert_pb2.ConfigurationResponse(
-                scid=dcid,
-                signal_id=log.signal_id,
-                retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig(mode=self.retransmission_mode)
-                ))
-        self.handle_configuration_request = handle_configuration_request
-        event_callback_stream.register_callback(self.handle_configuration_request, matcher_fn=is_configuration_request)
-
-        def handle_disconnection_request(log):
-            log = log.disconnection_request
-            self.cert_device.l2cap.SendDisconnectionResponse(l2cap_cert_pb2.DisconnectionResponse(dcid=log.dcid,scid=log.scid,
-                                                                                            signal_id=log.signal_id))
-        self.handle_disconnection_request = handle_disconnection_request
-        event_callback_stream.register_callback(self.handle_disconnection_request, matcher_fn=is_disconnection_request)
-
-        def handle_information_request(log):
-            log = log.information_request
-            self.cert_device.l2cap.SendInformationResponse(l2cap_cert_pb2.InformationResponse(type=log.type,
-                                                                                      signal_id=log.signal_id))
-        self.handle_information_request = handle_information_request
-        event_callback_stream.register_callback(self.handle_information_request, matcher_fn=is_information_request)
-
-        self.event_dump = []
-        def dump_log(log):
-            self.event_dump.append(log)
-        self.dump_log = dump_log
-        event_callback_stream.register_callback(self.dump_log)
-
-    def _setup_link(self, event_asserts):
-        self.cert_device.l2cap.SetupLink(l2cap_cert_pb2.SetupLinkRequest(remote=self.dut_address))
-        event_asserts.assert_event_occurs(lambda log : log.HasField("link_up") and log.remote == self.dut_address)
-
-    def _open_channel(self, event_asserts, scid=0x0101, psm=0x33):
-        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm))
-        self.cert_device.l2cap.SendConnectionRequest(l2cap_cert_pb2.ConnectionRequest(scid=scid, psm=psm))
-        event_asserts.assert_event_occurs(lambda log : is_configuration_response(log) and scid == log.scid)
-
-    def test_connect(self):
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self._setup_link(l2cap_event_asserts)
-            self._open_channel(l2cap_event_asserts, scid=0x0101)
-
-    def test_connect_and_send_data_ertm_no_segmentation(self):
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-
-            self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.ERTM
-            self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2))
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
-
-            self._setup_link(l2cap_event_asserts)
-            scid = 0x0101
-            self._open_channel(l2cap_event_asserts, scid=scid)
-
-            def on_data_received(log):
-                packet = log.data_packet
-                if (packet.channel == scid):
-                    self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=1, s=0))
-            l2cap_log_stream.register_callback(on_data_received, matcher_fn=lambda log : log.HasField("data_packet"))
-
-            self.device_under_test.l2cap.SendL2capPacket(l2cap_facade_pb2.L2capPacket(channel=2, payload=b"123"))
-            l2cap_event_asserts.assert_event_occurs(
-                lambda log : log.HasField("data_packet") and \
-                             log.data_packet.channel == 2 and \
-                             basic_frame_to_enhanced_information_frame(log.data_packet.payload) == b"123")
-
-            self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'*34))
-            self.cert_device.l2cap.SendIFrame(l2cap_cert_pb2.IFrame(channel=self.scid_dcid_map[scid], req_seq=1, tx_seq=0, sar=0, information=b"abcd"))
-            l2cap_event_asserts.assert_event_occurs(
-                lambda log : log.HasField("data_packet") and \
-                             log.data_packet.channel == scid and \
-                             basic_frame_to_enhanced_information_frame(log.data_packet.payload) == b"abc"*34)
-
-    def test_connect_and_send_data(self):
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-
-            self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2))
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33))
-
-            self._setup_link(l2cap_event_asserts)
-            scid = 0x0101
-            self._open_channel(l2cap_event_asserts, scid=scid)
-
-            self.device_under_test.l2cap.SendL2capPacket(l2cap_facade_pb2.L2capPacket(channel=2, payload=b"123"))
-            l2cap_event_asserts.assert_event_occurs(lambda log : log.HasField("data_packet") and log.data_packet.channel == 2 and log.data_packet.payload == b"123")
-
-            self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
-            l2cap_event_asserts.assert_event_occurs(lambda log : log.HasField("data_packet") and log.data_packet.channel == scid and log.data_packet.payload == b"abc")
-
-    def test_open_two_channels(self):
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self._setup_link(l2cap_event_asserts)
-            self._open_channel(l2cap_event_asserts, scid=0x0101, psm=0x1)
-            self._open_channel(l2cap_event_asserts, scid=0x0102, psm=0x3)
-
-    def test_accept_disconnect(self):
-        """
-        L2CAP/COS/CED/BV-07-C
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self._setup_link(l2cap_event_asserts)
-            scid=0x0101
-            self._open_channel(l2cap_event_asserts, scid=scid, psm=0x1)
-            dcid = self.scid_dcid_map[scid]
-            self.cert_device.l2cap.SendDisconnectionRequest(l2cap_cert_pb2.DisconnectionRequest(scid=scid, dcid=dcid, signal_id=2))
-            l2cap_event_asserts.assert_event_occurs(lambda log : is_disconnection_response(log) and log.disconnection_response.scid == scid and log.disconnection_response.dcid == dcid)
-
-    def test_disconnect_on_timeout(self):
-        """
-        L2CAP/COS/CED/BV-08-C
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self._setup_link(l2cap_event_asserts)
-            scid = 0x0101
-            psm = 1
-            self._open_channel(l2cap_event_asserts, scid=0x0101, psm=0x1)
-
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm))
-
-            # Don't send configuration response back
-            l2cap_log_stream.unregister_callback(self.handle_configuration_request, matcher_fn=is_configuration_request)
-
-            self.cert_device.l2cap.SendConnectionRequest(l2cap_cert_pb2.ConnectionRequest(scid=scid, psm=psm))
-            l2cap_event_asserts.assert_none_matching(is_configuration_response)
-
-    def test_basic_operation_request_connection(self):
-        """
-        L2CAP/COS/CED/BV-01-C [Request Connection]
-        Verify that the IUT is able to request the connection establishment for an L2CAP data channel and
-        initiate the configuration procedure.
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            psm = 1
-            # TODO: Use another test case
-            self.device_under_test.l2cap.OpenChannel(l2cap_facade_pb2.OpenChannelRequest(remote=self.cert_address, psm=psm))
-            l2cap_event_asserts.assert_event_occurs(lambda log : is_connection_request(log) and log.connection_request.psm == psm)
-
-    def test_respond_to_echo_request(self):
-        """
-        L2CAP/COS/ECH/BV-01-C [Respond to Echo Request]
-        Verify that the IUT responds to an echo request.
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self._setup_link(l2cap_event_asserts)
-            # TODO: Replace with constructed packets when PDL is available
-            echo_request_packet = b"\x08\x01\x00\x00"
-            self.cert_device.l2cap.SendL2capPacket(l2cap_facade_pb2.L2capPacket(channel=1, payload=echo_request_packet))
-            l2cap_event_asserts.assert_event_occurs(lambda log : is_echo_response(log) and log.echo_response.signal_id == 0x01)
-
-    def test_reject_unknown_command(self):
-        """
-        L2CAP/COS/CED/BI-01-C
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self._setup_link(l2cap_event_asserts)
-            # TODO: Replace with constructed packets when PDL is available
-            invalid_command_packet = b"\xff\x01\x00\x00"
-            self.cert_device.l2cap.SendL2capPacket(l2cap_facade_pb2.L2capPacket(channel=1, payload=invalid_command_packet))
-            # command_reject_packet = b"\x01\x01\x02\x00\x00\x00"
-            l2cap_event_asserts.assert_event_occurs(lambda log : is_command_reject(log) and log.command_reject.signal_id == 0x01)
-
-    def test_query_for_1_2_features(self):
-        """
-        L2CAP/COS/IEX/BV-01-C [Query for 1.2 Features]
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self._setup_link(l2cap_event_asserts)
-            signal_id = 3
-            self.cert_device.l2cap.SendInformationRequest(
-                l2cap_cert_pb2.InformationRequest(
-                    type=l2cap_cert_pb2.InformationRequestType.FIXED_CHANNELS, signal_id=signal_id))
-            l2cap_event_asserts.assert_event_occurs(
-                lambda log : is_information_response(log) and \
-                             log.information_response.signal_id == signal_id and \
-                             log.information_response.type == l2cap_cert_pb2.InformationRequestType.FIXED_CHANNELS)
-
-
-    def test_extended_feature_info_response_ertm(self):
-        """
-        L2CAP/EXF/BV-01-C [Extended Features Information Response for Enhanced
-        Retransmission Mode]
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            l2cap_event_asserts_alt = EventAsserts(l2cap_log_stream)
-
-            self._register_callbacks(l2cap_log_stream)
-            self._setup_link(l2cap_event_asserts)
-            signal_id = 3
-            self.cert_device.l2cap.SendInformationRequest(
-                l2cap_cert_pb2.InformationRequest(
-                    type=l2cap_cert_pb2.InformationRequestType.EXTENDED_FEATURES, signal_id=signal_id))
-
-            l2cap_event_asserts_alt.assert_event_occurs_at_most_times(is_information_response, 1)
-
-            expected_log_type = l2cap_cert_pb2.InformationRequestType.EXTENDED_FEATURES
-            expected_mask = 1 << 3
-            l2cap_event_asserts.assert_event_occurs(
-                lambda log : is_information_response(log) and \
-                    log.information_response.signal_id == signal_id and \
-                    log.information_response.type == expected_log_type and \
-                    log.information_response.information_value | expected_mask == expected_mask)
-
-    def test_transmit_i_frames(self):
-        """
-        L2CAP/ERM/BV-01-C [Transmit I-frames]
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            l2cap_event_asserts_alt = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.ERTM
-            self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2))
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
-            self._setup_link(l2cap_event_asserts)
-            scid = 0x0101
-            self._open_channel(l2cap_event_asserts, scid=scid)
-            self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
-            self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=1, s=0))
-            self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
-            self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=2, s=0))
-            self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
-            self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=3, s=0))
-            l2cap_event_asserts.assert_event_occurs(lambda log : log.HasField("data_packet") and log.data_packet.channel == 33 and log.data_packet.payload == b'abc')
-            l2cap_event_asserts.assert_event_occurs(lambda log : log.HasField("data_packet") and log.data_packet.channel == 33 and log.data_packet.payload == b'abc')
-            l2cap_event_asserts.assert_event_occurs(lambda log : log.HasField("data_packet") and log.data_packet.channel == 33 and log.data_packet.payload == b'abc')
-            l2cap_event_asserts_alt.assert_event_occurs_at_most(lambda log : log.HasField("data_packet"), 3)
-
-    def test_s_frame_transmissions_exceed_max_transmit(self):
-        """
-        L2CAP/ERM/BV-11-C [S-Frame Transmissions Exceed MaxTransmit]
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            l2cap_event_asserts_alt = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.ERTM
-            self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2))
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
-            self._setup_link(l2cap_event_asserts)
-            scid = 0x0101
-            self._open_channel(l2cap_event_asserts, scid=scid)
-            self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
-            # Retransmission timer = 1, 1 * monitor timer = 2, so total timeout is 3
-            time.sleep(4)
-            l2cap_event_asserts.assert_event_occurs(lambda log : is_disconnection_request(log) and \
-                                                        log.disconnection_request.dcid == scid and \
-                                                        log.disconnection_request.scid == self.scid_dcid_map[scid])
-            l2cap_event_asserts_alt.assert_event_occurs_at_most(lambda log : is_disconnection_request(log), 1)
-
-    def test_sent_rej_lost(self):
-        """
-        L2CAP/ERM/BI-01-C [S-Frame [REJ] Lost or Corrupted]
-        """
-        with EventCallbackStream(self.cert_device.l2cap.FetchL2capLog(empty_pb2.Empty())) as l2cap_log_stream:
-            l2cap_event_asserts = EventAsserts(l2cap_log_stream)
-            self._register_callbacks(l2cap_log_stream)
-            self._setup_link(l2cap_event_asserts)
-
-            signal_id = 3
-            scid = 0x0101
-            psm = 1
-            mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.ERTM
-            self.tx_window = 1
-            self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm, retransmission_mode=mode))
-            self.cert_device.l2cap.SendConnectionRequest(l2cap_cert_pb2.ConnectionRequest(scid=scid, psm=psm))
-
-            l2cap_log_stream.unregister_callback(self.handle_Connection_response, matcher_fn=is_configuration_response)
-            def handle_connection_response(log):
-                log = log.connection_response
-                self.scid_dcid_map[log.scid] = log.dcid
-                self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(
-                    dcid= self.scid_dcid_map[scid],
-                    signal_id=signal_id + 1,
-                    retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig(
-                        mode=mode
-                    )))
-                l2cap_log_stream.unregister_callback(handle_connection_response, matcher_fn=is_connection_response)
-            l2cap_log_stream.register_callback(handle_connection_response, matcher_fn=is_connection_response)
-
-            l2cap_log_stream.unregister_callback(self.handle_configuration_request, matcher_fn=is_configuration_request)
-            def handle_configuration_request(log):
-                log = log.configuration_request
-                if log.dcid not in self.scid_dcid_map:
-                    return
-                dcid = self.scid_dcid_map[log.dcid]
-                if log.HasField("retransmission_config"):
-                    self.tx_window = log.retransmission_config.tx_window
-                self.cert_device.l2cap.SendConfigurationResponse(l2cap_cert_pb2.ConfigurationResponse(
-                    scid=dcid,
-                    signal_id=log.signal_id,
-                    ))
-                l2cap_log_stream.unregister_callback(handle_configuration_request, matcher_fn=is_configuration_request)
-            l2cap_log_stream.register_callback(handle_configuration_request, matcher_fn=is_configuration_request)
-
-            self.cert_device.l2cap.SendIFrame(l2cap_cert_pb2.IFrame(channel=self.scid_dcid_map[scid], req_seq=0, tx_seq=0, sar=0))
-            self.cert_device.l2cap.SendIFrame(l2cap_cert_pb2.IFrame(channel=self.scid_dcid_map[scid], req_seq=0, tx_seq=(self.tx_window - 1), sar=0))
-            l2cap_event_asserts.assert_event_occurs(lambda  log : log.HasField("data_packet") and log.data_packet.channel == scid and log.data_packet.payload == b'\x05\x01')
-
-            self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=0, p=1, s=0))
-            l2cap_event_asserts.assert_event_occurs(lambda  log : log.HasField("data_packet") and log.data_packet.channel == scid and log.data_packet.payload == b'\x81\x01')
-
-            for i in range(1, self.tx_window):
-                self.cert_device.l2cap.SendIFrame(l2cap_cert_pb2.IFrame(channel=self.scid_dcid_map[scid], req_seq=0, tx_seq=(i), sar=0))
-                time.sleep(0.1)
-            l2cap_event_asserts.assert_event_occurs(lambda  log : log.HasField("data_packet") and log.data_packet.channel == scid and log.data_packet.payload == b'\x01\x0a')
\ No newline at end of file
diff --git a/gd/l2cap/classic/dynamic_channel_configuration_option.h b/gd/l2cap/classic/dynamic_channel_configuration_option.h
index 3f3cc02..e98a13a 100644
--- a/gd/l2cap/classic/dynamic_channel_configuration_option.h
+++ b/gd/l2cap/classic/dynamic_channel_configuration_option.h
@@ -30,6 +30,7 @@
   enum class RetransmissionAndFlowControlMode {
     L2CAP_BASIC,
     ENHANCED_RETRANSMISSION,
+    ENHANCED_RETRANSMISSION_OPTIONAL,
   };
   /**
    * Retransmission and flow control mode. Currently L2CAP_BASIC and ENHANCED_RETRANSMISSION.
diff --git a/gd/l2cap/classic/dynamic_channel_manager.cc b/gd/l2cap/classic/dynamic_channel_manager.cc
index 123fdd4..8620086 100644
--- a/gd/l2cap/classic/dynamic_channel_manager.cc
+++ b/gd/l2cap/classic/dynamic_channel_manager.cc
@@ -24,37 +24,35 @@
 namespace l2cap {
 namespace classic {
 
-bool DynamicChannelManager::ConnectChannel(hci::Address device, DynamicChannelConfigurationOption configuration_option,
-                                           Psm psm, OnConnectionOpenCallback on_connection_open,
-                                           OnConnectionFailureCallback on_fail_callback, os::Handler* handler) {
-  internal::Link::PendingDynamicChannelConnection pending_dynamic_channel_connection{
-      .handler_ = handler,
+void DynamicChannelManager::ConnectChannel(
+    hci::Address device,
+    DynamicChannelConfigurationOption configuration_option,
+    Psm psm,
+    OnConnectionOpenCallback on_connection_open,
+    OnConnectionFailureCallback on_fail_callback) {
+  internal::Link::PendingDynamicChannelConnection pending_connection{
       .on_open_callback_ = std::move(on_connection_open),
       .on_fail_callback_ = std::move(on_fail_callback),
       .configuration_ = configuration_option,
   };
-  l2cap_layer_handler_->Post(common::BindOnce(&internal::LinkManager::ConnectDynamicChannelServices,
-                                              common::Unretained(link_manager_), device,
-                                              std::move(pending_dynamic_channel_connection), psm));
-
-  return true;
+  l2cap_layer_handler_->CallOn(
+      link_manager_, &internal::LinkManager::ConnectDynamicChannelServices, device, std::move(pending_connection), psm);
 }
 
-bool DynamicChannelManager::RegisterService(Psm psm, DynamicChannelConfigurationOption configuration_option,
-                                            const SecurityPolicy& security_policy,
-                                            OnRegistrationCompleteCallback on_registration_complete,
-                                            OnConnectionOpenCallback on_connection_open, os::Handler* handler) {
+void DynamicChannelManager::RegisterService(
+    Psm psm,
+    DynamicChannelConfigurationOption configuration_option,
+    const classic::SecurityPolicy& security_policy,
+    OnRegistrationCompleteCallback on_registration_complete,
+    OnConnectionOpenCallback on_connection_open) {
   internal::DynamicChannelServiceImpl::PendingRegistration pending_registration{
-      .user_handler_ = handler,
+      .security_policy_ = security_policy,
       .on_registration_complete_callback_ = std::move(on_registration_complete),
       .on_connection_open_callback_ = std::move(on_connection_open),
       .configuration_ = configuration_option,
   };
-  l2cap_layer_handler_->Post(common::BindOnce(&internal::DynamicChannelServiceManagerImpl::Register,
-                                              common::Unretained(service_manager_), psm,
-                                              std::move(pending_registration)));
-
-  return true;
+  l2cap_layer_handler_->CallOn(
+      service_manager_, &internal::DynamicChannelServiceManagerImpl::Register, psm, std::move(pending_registration));
 }
 
 }  // namespace classic
diff --git a/gd/l2cap/classic/dynamic_channel_manager.h b/gd/l2cap/classic/dynamic_channel_manager.h
index 340d68d..66e3aee 100644
--- a/gd/l2cap/classic/dynamic_channel_manager.h
+++ b/gd/l2cap/classic/dynamic_channel_manager.h
@@ -17,14 +17,15 @@
 
 #include <string>
 
+#include "common/contextual_callback.h"
 #include "hci/acl_manager.h"
 #include "hci/address.h"
 #include "l2cap/classic/dynamic_channel.h"
 #include "l2cap/classic/dynamic_channel_configuration_option.h"
 #include "l2cap/classic/dynamic_channel_service.h"
+#include "l2cap/classic/security_policy.h"
 #include "l2cap/l2cap_packets.h"
 #include "l2cap/psm.h"
-#include "l2cap/security_policy.h"
 #include "os/handler.h"
 
 namespace bluetooth {
@@ -45,6 +46,8 @@
     FAIL_NO_SERVICE_REGISTERED = 1,  // No service is registered
     FAIL_HCI_ERROR = 2,              // See hci_error
     FAIL_L2CAP_ERROR = 3,            // See l2cap_connection_response_result
+    FAIL_REMOTE_NOT_SUPPORT = 4,     // Remote not support required retansmission and flow control mode
+    FAIL_SECURITY_BLOCK = 5,         // Cannot enhance required security level
   };
 
   struct ConnectionResult {
@@ -52,15 +55,10 @@
     hci::ErrorCode hci_error = hci::ErrorCode::SUCCESS;
     ConnectionResponseResult l2cap_connection_response_result = ConnectionResponseResult::SUCCESS;
   };
-  /**
-   * OnConnectionFailureCallback(std::string failure_reason);
-   */
-  using OnConnectionFailureCallback = common::OnceCallback<void(ConnectionResult result)>;
 
-  /**
-   * OnConnectionOpenCallback(DynamicChannel channel);
-   */
-  using OnConnectionOpenCallback = common::Callback<void(std::unique_ptr<DynamicChannel>)>;
+  using OnConnectionFailureCallback = common::ContextualOnceCallback<void(ConnectionResult result)>;
+
+  using OnConnectionOpenCallback = common::ContextualCallback<void(std::unique_ptr<DynamicChannel>)>;
 
   enum class RegistrationResult {
     SUCCESS = 0,
@@ -68,11 +66,8 @@
     FAIL_INVALID_SERVICE = 2,    // Invalid PSM
   };
 
-  /**
-   * OnRegistrationFailureCallback(RegistrationResult result, DynamicChannelService service);
-   */
   using OnRegistrationCompleteCallback =
-      common::OnceCallback<void(RegistrationResult, std::unique_ptr<DynamicChannelService>)>;
+      common::ContextualOnceCallback<void(RegistrationResult, std::unique_ptr<DynamicChannelService>)>;
 
   /**
    * Connect to a Dynamic channel on a remote device
@@ -89,14 +84,14 @@
    * @param psm: Service PSM to connect. PSM is defined in Core spec Vol 3 Part A 4.2.
    * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
    * @param on_fail_callback: A callback to indicate connection failure along with a status code.
-   * @param handler: The handler context in which to execute the @callback parameters.
    * @param configuration_option: The configuration options for this channel
-   *
-   * Returns: true if connection was able to be initiated, false otherwise.
    */
-  bool ConnectChannel(hci::Address device, DynamicChannelConfigurationOption configuration_option, Psm psm,
-                      OnConnectionOpenCallback on_connection_open, OnConnectionFailureCallback on_fail_callback,
-                      os::Handler* handler);
+  virtual void ConnectChannel(
+      hci::Address device,
+      DynamicChannelConfigurationOption configuration_option,
+      Psm psm,
+      OnConnectionOpenCallback on_connection_open,
+      OnConnectionFailureCallback on_fail_callback);
 
   /**
    * Register a service to receive incoming connections bound to a specific channel.
@@ -116,15 +111,22 @@
    * @param on_registration_complete: A callback to indicate the service setup has completed. If the return status is
    *        not SUCCESS, it means service is not registered due to reasons like PSM already take
    * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
-   * @param handler: The handler context in which to execute the @callback parameter.
    * @param configuration_option: The configuration options for this channel
    */
-  bool RegisterService(Psm psm, DynamicChannelConfigurationOption configuration_option,
-                       const SecurityPolicy& security_policy, OnRegistrationCompleteCallback on_registration_complete,
-                       OnConnectionOpenCallback on_connection_open, os::Handler* handler);
+  virtual void RegisterService(
+      Psm psm,
+      DynamicChannelConfigurationOption configuration_option,
+      const SecurityPolicy& security_policy,
+      OnRegistrationCompleteCallback on_registration_complete,
+      OnConnectionOpenCallback on_connection_open);
 
   friend class L2capClassicModule;
 
+  virtual ~DynamicChannelManager() = default;
+
+ protected:
+  DynamicChannelManager() = default;
+
  private:
   // The constructor is not to be used by user code
   DynamicChannelManager(internal::DynamicChannelServiceManagerImpl* service_manager,
diff --git a/gd/l2cap/classic/dynamic_channel_service.cc b/gd/l2cap/classic/dynamic_channel_service.cc
index ee97d37..4a2dfee 100644
--- a/gd/l2cap/classic/dynamic_channel_service.cc
+++ b/gd/l2cap/classic/dynamic_channel_service.cc
@@ -22,11 +22,10 @@
 namespace l2cap {
 namespace classic {
 
-void DynamicChannelService::Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler) {
+void DynamicChannelService::Unregister(OnUnregisteredCallback on_unregistered) {
   ASSERT_LOG(manager_ != nullptr, "this service is invalid");
-  l2cap_layer_handler_->Post(common::BindOnce(&internal::DynamicChannelServiceManagerImpl::Unregister,
-                                              common::Unretained(manager_), psm_, std::move(on_unregistered),
-                                              on_unregistered_handler));
+  l2cap_layer_handler_->CallOn(
+      manager_, &internal::DynamicChannelServiceManagerImpl::Unregister, psm_, std::move(on_unregistered));
 }
 
 Psm DynamicChannelService::GetPsm() const {
diff --git a/gd/l2cap/classic/dynamic_channel_service.h b/gd/l2cap/classic/dynamic_channel_service.h
index cd24b61..f098db1 100644
--- a/gd/l2cap/classic/dynamic_channel_service.h
+++ b/gd/l2cap/classic/dynamic_channel_service.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include "common/callback.h"
+#include "common/contextual_callback.h"
 #include "l2cap/psm.h"
 #include "os/handler.h"
 #include "os/log.h"
@@ -33,7 +33,7 @@
  public:
   DynamicChannelService() = default;
 
-  using OnUnregisteredCallback = common::OnceCallback<void()>;
+  using OnUnregisteredCallback = common::ContextualOnceCallback<void()>;
 
   /**
    * Unregister a service from L2CAP module. This operation cannot fail.
@@ -41,19 +41,21 @@
    *
    * @param on_unregistered will be triggered when unregistration is complete
    */
-  void Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler);
+  void Unregister(OnUnregisteredCallback on_unregistered);
 
   friend internal::DynamicChannelServiceManagerImpl;
 
   Psm GetPsm() const;
 
- private:
+ protected:
   DynamicChannelService(Psm psm, internal::DynamicChannelServiceManagerImpl* manager, os::Handler* handler)
       : psm_(psm), manager_(manager), l2cap_layer_handler_(handler) {
     ASSERT(IsPsmValid(psm));
     ASSERT(manager_ != nullptr);
     ASSERT(l2cap_layer_handler_ != nullptr);
   }
+
+ private:
   Psm psm_ = kDefaultPsm;
   internal::DynamicChannelServiceManagerImpl* manager_ = nullptr;
   os::Handler* l2cap_layer_handler_;
diff --git a/gd/l2cap/classic/facade.cc b/gd/l2cap/classic/facade.cc
index 11e72ae..1d8444d 100644
--- a/gd/l2cap/classic/facade.cc
+++ b/gd/l2cap/classic/facade.cc
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include <condition_variable>
 #include <cstdint>
 #include <unordered_map>
 
@@ -20,11 +22,9 @@
 #include "common/bind.h"
 #include "grpc/grpc_event_queue.h"
 #include "hci/address.h"
-#include "hci/facade.h"
 #include "l2cap/classic/facade.grpc.pb.h"
 #include "l2cap/classic/facade.h"
 #include "l2cap/classic/l2cap_classic_module.h"
-#include "l2cap/l2cap_packets.h"
 #include "os/log.h"
 #include "packet/raw_builder.h"
 
@@ -56,28 +56,6 @@
     return pending_connection_close_.RunLoop(context, writer);
   }
 
-  ::grpc::Status Connect(::grpc::ServerContext* context, const facade::BluetoothAddress* request,
-                         ::google::protobuf::Empty* response) override {
-    auto fixed_channel_manager = l2cap_layer_->GetFixedChannelManager();
-    hci::Address peer;
-    ASSERT(hci::Address::FromString(request->address(), peer));
-    fixed_channel_manager->ConnectServices(peer, common::BindOnce([](FixedChannelManager::ConnectionResult) {}),
-                                           facade_handler_);
-    return ::grpc::Status::OK;
-  }
-
-  ::grpc::Status SendL2capPacket(::grpc::ServerContext* context, const classic::L2capPacket* request,
-                                 SendL2capPacketResult* response) override {
-    std::unique_lock<std::mutex> lock(channel_map_mutex_);
-    if (fixed_channel_helper_map_.find(request->channel()) == fixed_channel_helper_map_.end()) {
-      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not registered");
-    }
-    std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
-    fixed_channel_helper_map_[request->channel()]->SendPacket(packet);
-    response->set_result_type(SendL2capPacketResultType::OK);
-    return ::grpc::Status::OK;
-  }
-
   ::grpc::Status SendDynamicChannelPacket(::grpc::ServerContext* context, const DynamicChannelPacket* request,
                                           ::google::protobuf::Empty* response) override {
     std::unique_lock<std::mutex> lock(channel_map_mutex_);
@@ -85,21 +63,22 @@
       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
     }
     std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
-    dynamic_channel_helper_map_[request->psm()]->SendPacket(packet);
+    if (!dynamic_channel_helper_map_[request->psm()]->SendPacket(packet)) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
+    }
     return ::grpc::Status::OK;
   }
 
   ::grpc::Status OpenChannel(::grpc::ServerContext* context,
                              const ::bluetooth::l2cap::classic::OpenChannelRequest* request,
                              ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(channel_map_mutex_);
-    auto psm = request->psm();
-    auto mode = request->mode();
-    dynamic_channel_helper_map_.emplace(
-        psm, std::make_unique<L2capDynamicChannelHelper>(this, l2cap_layer_, facade_handler_, psm, mode));
+    auto service_helper = dynamic_channel_helper_map_.find(request->psm());
+    if (service_helper == dynamic_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+    }
     hci::Address peer;
     ASSERT(hci::Address::FromString(request->remote().address(), peer));
-    dynamic_channel_helper_map_[psm]->Connect(peer);
+    dynamic_channel_helper_map_[request->psm()]->Connect(peer);
     return ::grpc::Status::OK;
   }
 
@@ -110,156 +89,17 @@
     if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
       return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
     }
-    dynamic_channel_helper_map_[psm]->disconnect();
+    dynamic_channel_helper_map_[psm]->Disconnect();
     return ::grpc::Status::OK;
   }
 
   ::grpc::Status FetchL2capData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
                                 ::grpc::ServerWriter<classic::L2capPacket>* writer) override {
-    {
-      std::unique_lock<std::mutex> lock(channel_map_mutex_);
-
-      for (auto& connection : fixed_channel_helper_map_) {
-        if (connection.second->channel_ != nullptr) {
-          connection.second->channel_->GetQueueUpEnd()->RegisterDequeue(
-              facade_handler_,
-              common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(connection.second.get())));
-        }
-      }
-
-      for (auto& connection : dynamic_channel_helper_map_) {
-        if (connection.second->channel_ != nullptr) {
-          connection.second->channel_->GetQueueUpEnd()->RegisterDequeue(
-              facade_handler_, common::Bind(&L2capDynamicChannelHelper::on_incoming_packet,
-                                            common::Unretained(connection.second.get())));
-        }
-      }
-
-      fetch_l2cap_data_ = true;
-    }
-
     auto status = pending_l2cap_data_.RunLoop(context, writer);
 
-    {
-      std::unique_lock<std::mutex> lock(channel_map_mutex_);
-
-      fetch_l2cap_data_ = false;
-
-      for (auto& connection : fixed_channel_helper_map_) {
-        if (connection.second->channel_ != nullptr) {
-          connection.second->channel_->GetQueueUpEnd()->RegisterDequeue(
-              facade_handler_,
-              common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(connection.second.get())));
-        }
-      }
-
-      for (auto& connection : dynamic_channel_helper_map_) {
-        if (connection.second->channel_ != nullptr) {
-          connection.second->channel_->GetQueueUpEnd()->RegisterDequeue(
-              facade_handler_, common::Bind(&L2capDynamicChannelHelper::on_incoming_packet,
-                                            common::Unretained(connection.second.get())));
-        }
-      }
-    }
-
     return status;
   }
 
-  ::grpc::Status RegisterChannel(::grpc::ServerContext* context, const classic::RegisterChannelRequest* request,
-                                 ::google::protobuf::Empty* response) override {
-    std::unique_lock<std::mutex> lock(channel_map_mutex_);
-    if (fixed_channel_helper_map_.find(request->channel()) != fixed_channel_helper_map_.end()) {
-      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Already registered");
-    }
-    fixed_channel_helper_map_.emplace(request->channel(), std::make_unique<L2capFixedChannelHelper>(
-                                                              this, l2cap_layer_, facade_handler_, request->channel()));
-
-    return ::grpc::Status::OK;
-  }
-
-  class L2capFixedChannelHelper {
-   public:
-    L2capFixedChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
-                            os::Handler* handler, Cid cid)
-        : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), cid_(cid) {
-      fixed_channel_manager_ = l2cap_layer_->GetFixedChannelManager();
-      fixed_channel_manager_->RegisterService(
-          cid, {},
-          common::BindOnce(&L2capFixedChannelHelper::on_l2cap_service_registration_complete, common::Unretained(this)),
-          common::Bind(&L2capFixedChannelHelper::on_connection_open, common::Unretained(this)), handler_);
-    }
-
-    void on_l2cap_service_registration_complete(FixedChannelManager::RegistrationResult registration_result,
-                                                std::unique_ptr<FixedChannelService> service) {
-      service_ = std::move(service);
-    }
-
-    void on_connection_open(std::unique_ptr<FixedChannel> channel) {
-      ConnectionCompleteEvent event;
-      event.mutable_remote()->set_address(channel->GetDevice().ToString());
-      facade_service_->pending_connection_complete_.OnIncomingEvent(event);
-      channel_ = std::move(channel);
-      channel_->RegisterOnCloseCallback(
-          facade_service_->facade_handler_,
-          common::BindOnce(&L2capFixedChannelHelper::on_close_callback, common::Unretained(this)));
-      {
-        std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
-        if (facade_service_->fetch_l2cap_data_) {
-          channel_->GetQueueUpEnd()->RegisterDequeue(
-              facade_service_->facade_handler_,
-              common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(this)));
-        }
-      }
-    }
-
-    void SendPacket(const std::vector<uint8_t>& packet) {
-      if (channel_ == nullptr) {
-        LOG_WARN("Channel is not open");
-        return;
-      }
-      channel_->GetQueueUpEnd()->RegisterEnqueue(
-          handler_, common::Bind(&L2capFixedChannelHelper::enqueue_callback, common::Unretained(this), packet));
-    }
-
-    void on_close_callback(hci::ErrorCode error_code) {
-      {
-        std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
-        if (facade_service_->fetch_l2cap_data_) {
-          channel_->GetQueueUpEnd()->UnregisterDequeue();
-        }
-      }
-      channel_ = nullptr;
-      classic::ConnectionCloseEvent event;
-      event.mutable_remote()->set_address(channel_->GetDevice().ToString());
-      event.set_reason(static_cast<uint32_t>(error_code));
-      facade_service_->pending_connection_close_.OnIncomingEvent(event);
-    }
-
-    void on_incoming_packet() {
-      auto packet = channel_->GetQueueUpEnd()->TryDequeue();
-      std::string data = std::string(packet->begin(), packet->end());
-      L2capPacket l2cap_data;
-      l2cap_data.set_channel(cid_);
-      l2cap_data.set_payload(data);
-      facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
-    }
-
-    std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(const std::vector<uint8_t>& packet) {
-      auto packet_one = std::make_unique<packet::RawBuilder>();
-      packet_one->AddOctets(packet);
-      channel_->GetQueueUpEnd()->UnregisterEnqueue();
-      return packet_one;
-    };
-
-    L2capClassicModuleFacadeService* facade_service_;
-    L2capClassicModule* l2cap_layer_;
-    os::Handler* handler_;
-    std::unique_ptr<FixedChannelManager> fixed_channel_manager_;
-    std::unique_ptr<FixedChannelService> service_;
-    std::unique_ptr<FixedChannel> channel_ = nullptr;
-    Cid cid_;
-  };
-
   ::grpc::Status SetDynamicChannel(::grpc::ServerContext* context, const SetEnableDynamicChannelRequest* request,
                                    google::protobuf::Empty* response) override {
     dynamic_channel_helper_map_.emplace(
@@ -268,71 +108,127 @@
     return ::grpc::Status::OK;
   }
 
+  ::grpc::Status SetTrafficPaused(::grpc::ServerContext* context, const SetTrafficPausedRequest* request,
+                                  ::google::protobuf::Empty* response) override {
+    auto psm = request->psm();
+    if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+    }
+    if (request->paused()) {
+      dynamic_channel_helper_map_[psm]->SuspendDequeue();
+    } else {
+      dynamic_channel_helper_map_[psm]->ResumeDequeue();
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status GetChannelQueueDepth(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                      GetChannelQueueDepthResponse* response) override {
+    // Use the value kChannelQueueSize (5) in internal/dynamic_channel_impl.h
+    response->set_size(5);
+    return ::grpc::Status::OK;
+  }
+
   class L2capDynamicChannelHelper {
    public:
     L2capDynamicChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
                               os::Handler* handler, Psm psm, RetransmissionFlowControlMode mode)
-        : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm) {
+        : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm), mode_(mode) {
       dynamic_channel_manager_ = l2cap_layer_->GetDynamicChannelManager();
       DynamicChannelConfigurationOption configuration_option = {};
-      if (mode == RetransmissionFlowControlMode::BASIC) {
-        configuration_option.channel_mode =
-            DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
-      } else if (mode == RetransmissionFlowControlMode::ERTM) {
-        configuration_option.channel_mode =
-            DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION;
-      }
+      configuration_option.channel_mode = (DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode)mode;
       dynamic_channel_manager_->RegisterService(
-          psm, configuration_option, {},
-          common::BindOnce(&L2capDynamicChannelHelper::on_l2cap_service_registration_complete,
-                           common::Unretained(this)),
-          common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)), handler_);
+          psm,
+          configuration_option,
+          SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK,
+          handler_->BindOnceOn(this, &L2capDynamicChannelHelper::on_l2cap_service_registration_complete),
+          handler_->BindOn(this, &L2capDynamicChannelHelper::on_connection_open));
+    }
+
+    ~L2capDynamicChannelHelper() {
+      if (dequeue_registered_) {
+        channel_->GetQueueUpEnd()->UnregisterDequeue();
+        channel_ = nullptr;
+      }
+      enqueue_buffer_.reset();
     }
 
     void Connect(hci::Address address) {
-      // TODO: specify channel mode
+      DynamicChannelConfigurationOption configuration_option = l2cap::classic::DynamicChannelConfigurationOption();
+      configuration_option.channel_mode = (DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode)mode_;
+
       dynamic_channel_manager_->ConnectChannel(
-          address, {}, psm_, common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)),
-          common::Bind(&L2capDynamicChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
+          address,
+          configuration_option,
+          psm_,
+          handler_->BindOn(this, &L2capDynamicChannelHelper::on_connection_open),
+          handler_->BindOnceOn(this, &L2capDynamicChannelHelper::on_connect_fail));
+      std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+      if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
+        LOG_WARN("Channel is not open for psm %d", psm_);
+      }
     }
 
-    void disconnect() {
+    void Disconnect() {
+      if (channel_ == nullptr) {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
+          LOG_WARN("Channel is not open for psm %d", psm_);
+          return;
+        }
+      }
       channel_->Close();
     }
 
     void on_l2cap_service_registration_complete(DynamicChannelManager::RegistrationResult registration_result,
                                                 std::unique_ptr<DynamicChannelService> service) {}
 
+    // invoked from Facade Handler
     void on_connection_open(std::unique_ptr<DynamicChannel> channel) {
       ConnectionCompleteEvent event;
-      event.mutable_remote()->set_address(channel->GetDevice().ToString());
+      event.mutable_remote()->set_address(channel->GetDevice().GetAddress().ToString());
       facade_service_->pending_connection_complete_.OnIncomingEvent(event);
-      channel_ = std::move(channel);
-      channel_->RegisterOnCloseCallback(
-          facade_service_->facade_handler_,
-          common::BindOnce(&L2capDynamicChannelHelper::on_close_callback, common::Unretained(this)));
       {
-        std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
-        if (facade_service_->fetch_l2cap_data_) {
-          channel_->GetQueueUpEnd()->RegisterDequeue(
-              facade_service_->facade_handler_,
-              common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
-        }
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        channel_ = std::move(channel);
+        enqueue_buffer_ = std::make_unique<os::EnqueueBuffer<BasePacketBuilder>>(channel_->GetQueueUpEnd());
       }
+      channel_open_cv_.notify_all();
+      channel_->RegisterOnCloseCallback(
+          facade_service_->facade_handler_->BindOnceOn(this, &L2capDynamicChannelHelper::on_close_callback));
+      dequeue_registered_ = true;
+      channel_->GetQueueUpEnd()->RegisterDequeue(
+          facade_service_->facade_handler_,
+          common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
     }
 
     void on_close_callback(hci::ErrorCode error_code) {
       {
-        std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
-        if (facade_service_->fetch_l2cap_data_) {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        if (dequeue_registered_.exchange(false)) {
           channel_->GetQueueUpEnd()->UnregisterDequeue();
         }
       }
-      channel_ = nullptr;
       classic::ConnectionCloseEvent event;
-      event.mutable_remote()->set_address(channel_->GetDevice().ToString());
+      event.mutable_remote()->set_address(channel_->GetDevice().GetAddress().ToString());
       event.set_reason(static_cast<uint32_t>(error_code));
       facade_service_->pending_connection_close_.OnIncomingEvent(event);
+      channel_ = nullptr;
+      enqueue_buffer_.reset();
+    }
+
+    void SuspendDequeue() {
+      if (dequeue_registered_.exchange(false)) {
+        channel_->GetQueueUpEnd()->UnregisterDequeue();
+      }
+    }
+
+    void ResumeDequeue() {
+      if (!dequeue_registered_.exchange(true)) {
+        channel_->GetQueueUpEnd()->RegisterDequeue(
+            facade_service_->facade_handler_,
+            common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
+      }
     }
 
     void on_connect_fail(DynamicChannelManager::ConnectionResult result) {}
@@ -341,42 +237,42 @@
       auto packet = channel_->GetQueueUpEnd()->TryDequeue();
       std::string data = std::string(packet->begin(), packet->end());
       L2capPacket l2cap_data;
-      //      l2cap_data.set_channel(cid_);
+      l2cap_data.set_psm(psm_);
       l2cap_data.set_payload(data);
       facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
     }
 
-    void SendPacket(std::vector<uint8_t> packet) {
+    bool SendPacket(std::vector<uint8_t> packet) {
       if (channel_ == nullptr) {
-        LOG_WARN("Channel is not open");
-        return;
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
+          LOG_WARN("Channel is not open");
+          return false;
+        }
       }
-      channel_->GetQueueUpEnd()->RegisterEnqueue(
-          handler_, common::Bind(&L2capDynamicChannelHelper::enqueue_callback, common::Unretained(this), packet));
-    }
-
-    std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet) {
       auto packet_one = std::make_unique<packet::RawBuilder>(2000);
       packet_one->AddOctets(packet);
-      channel_->GetQueueUpEnd()->UnregisterEnqueue();
-      return packet_one;
-    };
-
+      enqueue_buffer_->Enqueue(std::move(packet_one), handler_);
+      return true;
+    }
     L2capClassicModuleFacadeService* facade_service_;
     L2capClassicModule* l2cap_layer_;
     os::Handler* handler_;
     std::unique_ptr<DynamicChannelManager> dynamic_channel_manager_;
     std::unique_ptr<DynamicChannelService> service_;
     std::unique_ptr<DynamicChannel> channel_ = nullptr;
+    std::unique_ptr<os::EnqueueBuffer<BasePacketBuilder>> enqueue_buffer_ = nullptr;
     Psm psm_;
+    RetransmissionFlowControlMode mode_ = RetransmissionFlowControlMode::BASIC;
+    std::atomic_bool dequeue_registered_ = false;
+    std::condition_variable channel_open_cv_;
+    std::mutex channel_open_cv_mutex_;
   };
 
   L2capClassicModule* l2cap_layer_;
   ::bluetooth::os::Handler* facade_handler_;
   std::mutex channel_map_mutex_;
-  std::map<Cid, std::unique_ptr<L2capFixedChannelHelper>> fixed_channel_helper_map_;
   std::map<Psm, std::unique_ptr<L2capDynamicChannelHelper>> dynamic_channel_helper_map_;
-  bool fetch_l2cap_data_ = false;
   ::bluetooth::grpc::GrpcEventQueue<classic::ConnectionCompleteEvent> pending_connection_complete_{
       "FetchConnectionComplete"};
   ::bluetooth::grpc::GrpcEventQueue<classic::ConnectionCloseEvent> pending_connection_close_{"FetchConnectionClose"};
@@ -386,13 +282,10 @@
 void L2capClassicModuleFacadeModule::ListDependencies(ModuleList* list) {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<l2cap::classic::L2capClassicModule>();
-  list->add<hci::HciLayer>();
 }
 
 void L2capClassicModuleFacadeModule::Start() {
   ::bluetooth::grpc::GrpcFacadeModule::Start();
-  GetDependency<hci::HciLayer>()->EnqueueCommand(hci::WriteScanEnableBuilder::Create(hci::ScanEnable::PAGE_SCAN_ONLY),
-                                                 common::BindOnce([](hci::CommandCompleteView) {}), GetHandler());
   service_ = new L2capClassicModuleFacadeService(GetDependency<l2cap::classic::L2capClassicModule>(), GetHandler());
 }
 
diff --git a/gd/l2cap/classic/facade.proto b/gd/l2cap/classic/facade.proto
index b5faf03..d9e80d5 100644
--- a/gd/l2cap/classic/facade.proto
+++ b/gd/l2cap/classic/facade.proto
@@ -6,23 +6,22 @@
 import "facade/common.proto";
 
 service L2capClassicModuleFacade {
-  rpc RegisterChannel(RegisterChannelRequest) returns (google.protobuf.Empty) {
-    // Testing Android Bluetooth stack only. Optional for other stack.
-  }
   rpc FetchConnectionComplete(google.protobuf.Empty) returns (stream ConnectionCompleteEvent) {
     // Testing Android Bluetooth stack only. Optional for other stack.
   }
   rpc FetchConnectionClose(google.protobuf.Empty) returns (stream ConnectionCloseEvent) {
     // Testing Android Bluetooth stack only. Optional for other stack.
   }
-  rpc Connect(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
   rpc OpenChannel(OpenChannelRequest) returns (google.protobuf.Empty) {}
   rpc CloseChannel(CloseChannelRequest) returns (google.protobuf.Empty) {}
-  rpc ConfigureChannel(ConfigureChannelRequest) returns (google.protobuf.Empty) {}
-  rpc SendL2capPacket(L2capPacket) returns (SendL2capPacketResult) {}
   rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {}
   rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {}
   rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {}
+  rpc SetTrafficPaused(SetTrafficPausedRequest) returns (google.protobuf.Empty) {}
+  rpc GetChannelQueueDepth(google.protobuf.Empty) returns (GetChannelQueueDepthResponse) {
+    // Get the buffer size of channel queue end for L2CAP user (how many packets we can buffer
+    // before L2CAP user dequeues.
+  }
 }
 
 message RegisterChannelRequest {
@@ -40,7 +39,8 @@
 
 enum RetransmissionFlowControlMode {
   BASIC = 0;
-  ERTM = 3;
+  ERTM = 1;
+  ERTM_OPTIONAL = 2;
 }
 
 message OpenChannelRequest {
@@ -49,11 +49,6 @@
   RetransmissionFlowControlMode mode = 3;
 }
 
-message ConfigureChannelRequest {
-  facade.BluetoothAddress remote = 1;
-  // Config
-}
-
 message CloseChannelRequest {
   uint32 psm = 1;
 }
@@ -79,8 +74,10 @@
 }
 
 message L2capPacket {
-  facade.BluetoothAddress remote = 1;
-  uint32 channel = 2;
+  oneof channel_type {
+    uint32 psm = 1;
+    uint32 fixed_cid = 2;
+  }
   bytes payload = 3;
 }
 
@@ -95,3 +92,19 @@
   uint32 psm = 2;
   bytes payload = 3;
 }
+
+message SetTrafficPausedRequest {
+  bool paused = 1;
+  uint32 psm = 2;
+}
+
+message GetChannelQueueDepthResponse {
+  uint32 size = 1;
+}
+
+enum ClassicSecurityPolicy {
+  ENCRYPTED_TRANSPORT = 0;
+  AUTHENTICATED_ENCRYPTED_TRANSPORT = 1;
+  BEST = 2;
+  _SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK = 3;
+}
diff --git a/gd/l2cap/classic/fixed_channel_manager.cc b/gd/l2cap/classic/fixed_channel_manager.cc
index 0c3e5c7..24e78bd 100644
--- a/gd/l2cap/classic/fixed_channel_manager.cc
+++ b/gd/l2cap/classic/fixed_channel_manager.cc
@@ -35,8 +35,7 @@
   return true;
 }
 
-bool FixedChannelManager::RegisterService(Cid cid, const SecurityPolicy& security_policy,
-                                          OnRegistrationCompleteCallback on_registration_complete,
+bool FixedChannelManager::RegisterService(Cid cid, OnRegistrationCompleteCallback on_registration_complete,
                                           OnConnectionOpenCallback on_connection_open, os::Handler* handler) {
   internal::FixedChannelServiceImpl::PendingRegistration pending_registration{
       .user_handler_ = handler,
diff --git a/gd/l2cap/classic/fixed_channel_manager.h b/gd/l2cap/classic/fixed_channel_manager.h
index 65c0b73..a3285e7 100644
--- a/gd/l2cap/classic/fixed_channel_manager.h
+++ b/gd/l2cap/classic/fixed_channel_manager.h
@@ -22,7 +22,6 @@
 #include "l2cap/cid.h"
 #include "l2cap/classic/fixed_channel.h"
 #include "l2cap/classic/fixed_channel_service.h"
-#include "l2cap/security_policy.h"
 #include "os/handler.h"
 
 namespace bluetooth {
@@ -122,14 +121,12 @@
    * - on_open_callback, will only be triggered after on_service_registered callback
    *
    * @param cid:  cid used to receive incoming connections
-   * @param security_policy: The security policy used for the connection.
    * @param on_registration_complete: A callback to indicate the service setup has completed. If the return status is
    *        not SUCCESS, it means service is not registered due to reasons like CID already take
    * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
    * @param handler: The handler context in which to execute the @callback parameter.
    */
-  virtual bool RegisterService(Cid cid, const SecurityPolicy& security_policy,
-                               OnRegistrationCompleteCallback on_registration_complete,
+  virtual bool RegisterService(Cid cid, OnRegistrationCompleteCallback on_registration_complete,
                                OnConnectionOpenCallback on_connection_open, os::Handler* handler);
 
   virtual ~FixedChannelManager() = default;
diff --git a/gd/l2cap/classic/fixed_channel_manager_mock.h b/gd/l2cap/classic/fixed_channel_manager_mock.h
index 5ead957..513213e 100644
--- a/gd/l2cap/classic/fixed_channel_manager_mock.h
+++ b/gd/l2cap/classic/fixed_channel_manager_mock.h
@@ -31,7 +31,7 @@
   MOCK_METHOD(bool, ConnectServices,
               (hci::Address device, OnConnectionFailureCallback on_fail_callback, os::Handler* handler), (override));
   MOCK_METHOD(bool, RegisterService,
-              (Cid cid, const SecurityPolicy& security_policy, OnRegistrationCompleteCallback on_registration_complete,
+              (Cid cid, OnRegistrationCompleteCallback on_registration_complete,
                OnConnectionOpenCallback on_connection_open, os::Handler* handler),
               (override));
 };
diff --git a/gd/l2cap/classic/fixed_channel_mock.h b/gd/l2cap/classic/fixed_channel_mock.h
new file mode 100644
index 0000000..417e776
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel_mock.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/classic/fixed_channel.h"
+#include "l2cap/classic/internal/fixed_channel_impl_mock.h"
+#include "os/handler.h"
+
+#include <gmock/gmock.h>
+
+#include <utility>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace testing {
+
+class MockFixedChannel : public FixedChannel {
+ public:
+  MockFixedChannel() : FixedChannel(nullptr, nullptr){};
+  MOCK_METHOD(void, Acquire, ());
+  MOCK_METHOD(void, Release, ());
+  MOCK_METHOD(void, RegisterOnCloseCallback, (os::Handler * handler, OnCloseCallback on_close_callback));
+};
+
+}  // namespace testing
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/security_policy.h b/gd/l2cap/classic/fixed_channel_service_mock.h
similarity index 62%
copy from gd/l2cap/security_policy.h
copy to gd/l2cap/classic/fixed_channel_service_mock.h
index 5a06401..6695cd1 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/l2cap/classic/fixed_channel_service_mock.h
@@ -15,10 +15,23 @@
  */
 #pragma once
 
+#include "l2cap/classic/fixed_channel_service.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
 namespace bluetooth {
 namespace l2cap {
+namespace classic {
+namespace testing {
 
-class SecurityPolicy {};
+class MockFixedChannelService : public FixedChannelService {
+ public:
+  MockFixedChannelService() : FixedChannelService(){};
+  MOCK_METHOD(void, Unregister, (OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler));
+};
 
+}  // namespace testing
+}  // namespace classic
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_impl.h b/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
index 2fcf3fc..b444c7b 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
@@ -22,6 +22,7 @@
 #include "l2cap/classic/dynamic_channel_configuration_option.h"
 #include "l2cap/classic/dynamic_channel_manager.h"
 #include "l2cap/classic/dynamic_channel_service.h"
+#include "l2cap/classic/security_policy.h"
 
 namespace bluetooth {
 namespace l2cap {
@@ -33,31 +34,38 @@
 
   struct PendingRegistration {
     os::Handler* user_handler_ = nullptr;
+    classic::SecurityPolicy security_policy_;
     DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete_callback_;
     DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
     DynamicChannelConfigurationOption configuration_;
   };
 
   virtual void NotifyChannelCreation(std::unique_ptr<DynamicChannel> channel) {
-    user_handler_->Post(common::BindOnce(on_connection_open_callback_, std::move(channel)));
+    on_connection_open_callback_.Invoke(std::move(channel));
   }
 
-  DynamicChannelConfigurationOption GetConfigOption() const {
+  virtual DynamicChannelConfigurationOption GetConfigOption() const {
     return config_option_;
   }
 
+  virtual SecurityPolicy GetSecurityPolicy() const {
+    return security_policy_;
+  }
+
   friend class DynamicChannelServiceManagerImpl;
 
  protected:
   // protected access for mocking
-  DynamicChannelServiceImpl(os::Handler* user_handler,
-                            DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback,
-                            DynamicChannelConfigurationOption config_option)
-      : user_handler_(user_handler), on_connection_open_callback_(std::move(on_connection_open_callback)),
+  DynamicChannelServiceImpl(
+      classic::SecurityPolicy security_policy,
+      DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback,
+      DynamicChannelConfigurationOption config_option)
+      : security_policy_(security_policy),
+        on_connection_open_callback_(std::move(on_connection_open_callback)),
         config_option_(config_option) {}
 
  private:
-  os::Handler* user_handler_ = nullptr;
+  classic::SecurityPolicy security_policy_;
   DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
   DynamicChannelConfigurationOption config_option_;
 };
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_impl_mock.h b/gd/l2cap/classic/internal/dynamic_channel_service_impl_mock.h
new file mode 100644
index 0000000..299841e
--- /dev/null
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_impl_mock.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include "l2cap/classic/internal/dynamic_channel_service_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace testing {
+
+class MockDynamicChannelServiceImpl : public DynamicChannelServiceImpl {
+ public:
+  MockDynamicChannelServiceImpl() : DynamicChannelServiceImpl({}, {}, {}) {}
+  MOCK_METHOD(SecurityPolicy, GetSecurityPolicy, (), (const, override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc
index a44a72e..1aa4f7e 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc
@@ -29,31 +29,29 @@
                                                 DynamicChannelServiceImpl::PendingRegistration pending_registration) {
   if (!IsPsmValid(psm)) {
     std::unique_ptr<DynamicChannelService> invalid_service(new DynamicChannelService());
-    pending_registration.user_handler_->Post(
-        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
-                         DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE, std::move(invalid_service)));
+    pending_registration.on_registration_complete_callback_.Invoke(
+        DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE, std::move(invalid_service));
   } else if (IsServiceRegistered(psm)) {
     std::unique_ptr<DynamicChannelService> invalid_service(new DynamicChannelService());
-    pending_registration.user_handler_->Post(common::BindOnce(
-        std::move(pending_registration.on_registration_complete_callback_),
-        DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE, std::move(invalid_service)));
+    pending_registration.on_registration_complete_callback_.Invoke(
+        DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE, std::move(invalid_service));
   } else {
-    service_map_.try_emplace(psm,
-                             DynamicChannelServiceImpl(pending_registration.user_handler_,
-                                                       std::move(pending_registration.on_connection_open_callback_),
-                                                       pending_registration.configuration_));
+    service_map_.try_emplace(
+        psm,
+        DynamicChannelServiceImpl(
+            pending_registration.security_policy_,
+            std::move(pending_registration.on_connection_open_callback_),
+            pending_registration.configuration_));
     std::unique_ptr<DynamicChannelService> user_service(new DynamicChannelService(psm, this, l2cap_layer_handler_));
-    pending_registration.user_handler_->Post(
-        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
-                         DynamicChannelManager::RegistrationResult::SUCCESS, std::move(user_service)));
+    pending_registration.on_registration_complete_callback_.Invoke(
+        DynamicChannelManager::RegistrationResult::SUCCESS, std::move(user_service));
   }
 }
 
-void DynamicChannelServiceManagerImpl::Unregister(Psm psm, DynamicChannelService::OnUnregisteredCallback callback,
-                                                  os::Handler* handler) {
+void DynamicChannelServiceManagerImpl::Unregister(Psm psm, DynamicChannelService::OnUnregisteredCallback callback) {
   if (IsServiceRegistered(psm)) {
     service_map_.erase(psm);
-    handler->Post(std::move(callback));
+    callback.Invoke();
   } else {
     LOG_ERROR("service not registered psm:%d", psm);
   }
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.h b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.h
index c980639..724d70a 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.h
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.h
@@ -20,6 +20,7 @@
 
 #include "l2cap/classic/dynamic_channel_service.h"
 #include "l2cap/classic/internal/dynamic_channel_service_impl.h"
+#include "l2cap/classic/security_enforcement_interface.h"
 #include "l2cap/psm.h"
 #include "os/handler.h"
 #include "os/log.h"
@@ -39,14 +40,25 @@
   // All APIs must be invoked in L2CAP layer handler
   //
   virtual void Register(Psm psm, DynamicChannelServiceImpl::PendingRegistration pending_registration);
-  virtual void Unregister(Psm psm, DynamicChannelService::OnUnregisteredCallback callback, os::Handler* handler);
+  virtual void Unregister(Psm psm, DynamicChannelService::OnUnregisteredCallback callback);
   virtual bool IsServiceRegistered(Psm psm) const;
   virtual DynamicChannelServiceImpl* GetService(Psm psm);
 
   virtual std::vector<std::pair<Psm, DynamicChannelServiceImpl*>> GetRegisteredServices();
+
+  // Implementation is set by SecurityManager through L2capModule
+  void SetSecurityEnforcementInterface(SecurityEnforcementInterface* impl) {
+    security_enforcement_interface_ = impl;
+  }
+
+  virtual SecurityEnforcementInterface* GetSecurityEnforcementInterface() {
+    return security_enforcement_interface_;
+  }
+
  private:
   os::Handler* l2cap_layer_handler_ = nullptr;
   std::unordered_map<Psm, DynamicChannelServiceImpl> service_map_;
+  SecurityEnforcementInterface* security_enforcement_interface_ = nullptr;
 };
 }  // namespace internal
 }  // namespace classic
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h
index 05d4b7f..eb25d29 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h
@@ -16,6 +16,7 @@
 #pragma once
 
 #include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/classic/security_enforcement_interface.h"
 #include "l2cap/internal/dynamic_channel_impl.h"
 
 #include <gmock/gmock.h>
@@ -32,15 +33,15 @@
   MockDynamicChannelServiceManagerImpl() : DynamicChannelServiceManagerImpl(nullptr) {}
   MOCK_METHOD(void, Register, (Psm psm, DynamicChannelServiceImpl::PendingRegistration pending_registration),
               (override));
-  MOCK_METHOD(void, Unregister, (Psm psm, DynamicChannelService::OnUnregisteredCallback callback, os::Handler* handler),
-              (override));
+  MOCK_METHOD(void, Unregister, (Psm psm, DynamicChannelService::OnUnregisteredCallback callback), (override));
   MOCK_METHOD(bool, IsServiceRegistered, (Psm psm), (const, override));
   MOCK_METHOD(DynamicChannelServiceImpl*, GetService, (Psm psm), (override));
   MOCK_METHOD((std::vector<std::pair<Psm, DynamicChannelServiceImpl*>>), GetRegisteredServices, (), (override));
+  MOCK_METHOD(SecurityEnforcementInterface*, GetSecurityEnforcementInterface, (), (override));
 };
 
 }  // namespace testing
 }  // namespace internal
 }  // namespace classic
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_manager_test.cc b/gd/l2cap/classic/internal/dynamic_channel_service_manager_test.cc
index f59dcb6..f2bed12 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_service_manager_test.cc
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_manager_test.cc
@@ -30,6 +30,7 @@
 namespace l2cap {
 namespace classic {
 namespace internal {
+namespace {
 
 class L2capDynamicServiceManagerTest : public ::testing::Test {
  public:
@@ -61,8 +62,9 @@
   void sync_user_handler() {
     std::promise<void> promise;
     auto future = promise.get_future();
-    user_handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
-    future.wait_for(std::chrono::milliseconds(3));
+    user_handler_->CallOn(&promise, &std::promise<void>::set_value);
+    auto future_status = future.wait_for(std::chrono::seconds(1));
+    EXPECT_EQ(future_status, std::future_status::ready);
   }
 
   DynamicChannelServiceManagerImpl* manager_ = nullptr;
@@ -76,23 +78,24 @@
 TEST_F(L2capDynamicServiceManagerTest, register_and_unregister_classic_dynamic_channel) {
   DynamicChannelServiceImpl::PendingRegistration pending_registration{
       .user_handler_ = user_handler_,
+      .security_policy_ = SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK,
       .on_registration_complete_callback_ =
-          common::BindOnce(&L2capDynamicServiceManagerTest::OnServiceRegistered, common::Unretained(this), true)};
+          user_handler_->BindOnceOn(this, &L2capDynamicServiceManagerTest::OnServiceRegistered, true)};
   Cid cid = kSmpBrCid;
   EXPECT_FALSE(manager_->IsServiceRegistered(cid));
   manager_->Register(cid, std::move(pending_registration));
   EXPECT_TRUE(manager_->IsServiceRegistered(cid));
   sync_user_handler();
   EXPECT_TRUE(service_registered_);
-  manager_->Unregister(cid, common::BindOnce([] {}), user_handler_);
+  manager_->Unregister(cid, user_handler_->BindOnce([] {}));
   EXPECT_FALSE(manager_->IsServiceRegistered(cid));
 }
 
 TEST_F(L2capDynamicServiceManagerTest, register_classic_dynamic_channel_bad_cid) {
   DynamicChannelServiceImpl::PendingRegistration pending_registration{
-      .user_handler_ = user_handler_,
+      .security_policy_ = SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK,
       .on_registration_complete_callback_ =
-          common::BindOnce(&L2capDynamicServiceManagerTest::OnServiceRegistered, common::Unretained(this), false)};
+          user_handler_->BindOnceOn(this, &L2capDynamicServiceManagerTest::OnServiceRegistered, false)};
   Cid cid = 0x1000;
   EXPECT_FALSE(manager_->IsServiceRegistered(cid));
   manager_->Register(cid, std::move(pending_registration));
@@ -101,6 +104,7 @@
   EXPECT_FALSE(service_registered_);
 }
 
+}  // namespace
 }  // namespace internal
 }  // namespace classic
 }  // namespace l2cap
diff --git a/gd/l2cap/classic/internal/fixed_channel_impl.cc b/gd/l2cap/classic/internal/fixed_channel_impl.cc
index 9bfaef8..1ab5034 100644
--- a/gd/l2cap/classic/internal/fixed_channel_impl.cc
+++ b/gd/l2cap/classic/internal/fixed_channel_impl.cc
@@ -19,7 +19,6 @@
 #include "l2cap/cid.h"
 #include "l2cap/classic/internal/fixed_channel_impl.h"
 #include "l2cap/classic/internal/link.h"
-#include "l2cap/security_policy.h"
 #include "os/handler.h"
 #include "os/log.h"
 
diff --git a/gd/l2cap/classic/internal/fixed_channel_impl_test.cc b/gd/l2cap/classic/internal/fixed_channel_impl_test.cc
index 3a35d82..efd3798 100644
--- a/gd/l2cap/classic/internal/fixed_channel_impl_test.cc
+++ b/gd/l2cap/classic/internal/fixed_channel_impl_test.cc
@@ -30,6 +30,7 @@
 namespace internal {
 
 using l2cap::internal::testing::MockParameterProvider;
+using ::testing::_;
 using testing::MockLink;
 using ::testing::Return;
 
@@ -39,7 +40,7 @@
     std::promise<void> promise;
     auto future = promise.get_future();
     handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
-    future.wait_for(std::chrono::milliseconds(3));
+    future.wait_for(std::chrono::seconds(1));
   }
 
  protected:
@@ -60,7 +61,14 @@
 
 TEST_F(L2capClassicFixedChannelImplTest, get_device) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection),
+                             nullptr /* LinkManager */);
   hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                               hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
@@ -70,7 +78,14 @@
 
 TEST_F(L2capClassicFixedChannelImplTest, close_triggers_callback) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection),
+                             nullptr /* LinkManager */);
   hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                               hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
@@ -92,7 +107,14 @@
 
 TEST_F(L2capClassicFixedChannelImplTest, register_callback_after_close_should_call_immediately) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection),
+                             nullptr /* LinkManager */);
   hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                               hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
@@ -113,8 +135,16 @@
 }
 
 TEST_F(L2capClassicFixedChannelImplTest, close_twice_should_fail) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection),
+                             nullptr /* LinkManager */);
   hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                               hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
@@ -132,14 +162,22 @@
   EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
 
   // 2nd OnClose() callback should fail
-  EXPECT_DEATH(fixed_channel_impl.OnClosed(hci::ErrorCode::PAGE_TIMEOUT), ".*OnClosed.*");
+  EXPECT_DEATH(fixed_channel_impl.OnClosed(hci::ErrorCode::PAGE_TIMEOUT), ".*assertion \'!closed_\' failed.*");
 
   user_handler->Clear();
 }
 
-TEST_F(L2capClassicFixedChannelImplTest, multiple_registeration_should_fail) {
+TEST_F(L2capClassicFixedChannelImplTest, multiple_registration_should_fail) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection),
+                             nullptr /* LinkManager */);
   hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                               hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
@@ -153,34 +191,59 @@
 
   EXPECT_DEATH(fixed_channel_impl.RegisterOnCloseCallback(user_handler.get(),
                                                           common::BindOnce([](hci::ErrorCode status) { FAIL(); })),
-               ".*RegisterOnCloseCallback.*");
+               ".*OnCloseCallback can only be registered once.*");
 
   user_handler->Clear();
 }
 
-TEST_F(L2capClassicFixedChannelImplTest, call_acquire_before_registeration_should_fail) {
+TEST_F(L2capClassicFixedChannelImplTest, call_acquire_before_registration_should_fail) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection),
+                             nullptr /* LinkManager */);
   hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                               hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
-  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Acquire.*");
+  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Must register OnCloseCallback before calling any methods.*");
 }
 
-TEST_F(L2capClassicFixedChannelImplTest, call_release_before_registeration_should_fail) {
+TEST_F(L2capClassicFixedChannelImplTest, call_release_before_registration_should_fail) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection),
+                             nullptr /* LinkManager */);
   hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                               hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
-  EXPECT_DEATH(fixed_channel_impl.Release(), ".*Release.*");
+  EXPECT_DEATH(fixed_channel_impl.Release(), ".*Must register OnCloseCallback before calling any methods.*");
 }
 
 TEST_F(L2capClassicFixedChannelImplTest, test_acquire_release_channel) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection),
+                             nullptr /* LinkManager */);
   hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                               hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
@@ -190,7 +253,8 @@
   auto user_handler = std::make_unique<os::Handler>(thread_);
   hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
   fixed_channel_impl.RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+      user_handler.get(),
+      common::testing::BindLambdaForTesting([&my_status](hci::ErrorCode status) { my_status = status; }));
 
   // Default should be false
   EXPECT_FALSE(fixed_channel_impl.IsAcquired());
@@ -208,8 +272,16 @@
 }
 
 TEST_F(L2capClassicFixedChannelImplTest, test_acquire_after_close) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockClassicAclConnection* mock_acl_connection = new testing::MockClassicAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockClassicAclConnection>(mock_acl_connection),
+                             nullptr /* LinkManager */);
   hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                               hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
@@ -218,18 +290,25 @@
   // Register on close callback
   auto user_handler = std::make_unique<os::Handler>(thread_);
   hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
-  fixed_channel_impl.RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  fixed_channel_impl.RegisterOnCloseCallback(user_handler.get(),
+                                             common::testing::BindLambdaForTesting([&](hci::ErrorCode status) {
+                                               my_status = status;
+                                               promise.set_value();
+                                             }));
 
   // Channel closure should trigger such callback
   fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
   SyncHandler(user_handler.get());
+  auto future_status = future.wait_for(std::chrono::seconds(1));
+  EXPECT_EQ(future_status, std::future_status::ready);
   EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
 
   // Release or Acquire after closing should crash
   EXPECT_CALL(mock_classic_link, RefreshRefCount()).Times(0);
   EXPECT_FALSE(fixed_channel_impl.IsAcquired());
-  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Acquire.*");
+  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Must register OnCloseCallback before calling any methods.*");
 
   user_handler->Clear();
 }
diff --git a/gd/l2cap/classic/internal/link.cc b/gd/l2cap/classic/internal/link.cc
index 18e6703..ce700ef 100644
--- a/gd/l2cap/classic/internal/link.cc
+++ b/gd/l2cap/classic/internal/link.cc
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
+#include "l2cap/classic/internal/link.h"
+
 #include <chrono>
 #include <memory>
 
-#include "hci/acl_manager.h"
+#include "common/bind.h"
+#include "hci/acl_manager/classic_acl_connection.h"
 #include "l2cap/classic/dynamic_channel_manager.h"
 #include "l2cap/classic/internal/fixed_channel_impl.h"
-#include "l2cap/classic/internal/link.h"
+#include "l2cap/classic/internal/link_manager.h"
 #include "l2cap/internal/parameter_provider.h"
 #include "os/alarm.h"
 
@@ -29,14 +32,18 @@
 namespace classic {
 namespace internal {
 
-Link::Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
+using RetransmissionAndFlowControlMode = DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode;
+using ConnectionResult = DynamicChannelManager::ConnectionResult;
+using ConnectionResultCode = DynamicChannelManager::ConnectionResultCode;
+
+Link::Link(os::Handler* l2cap_handler, std::unique_ptr<hci::acl_manager::ClassicAclConnection> acl_connection,
            l2cap::internal::ParameterProvider* parameter_provider,
            DynamicChannelServiceManagerImpl* dynamic_service_manager,
-           FixedChannelServiceManagerImpl* fixed_service_manager)
+           FixedChannelServiceManagerImpl* fixed_service_manager, LinkManager* link_manager)
     : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)),
       data_pipeline_manager_(l2cap_handler, this, acl_connection_->GetAclQueueEnd()),
       parameter_provider_(parameter_provider), dynamic_service_manager_(dynamic_service_manager),
-      fixed_service_manager_(fixed_service_manager),
+      fixed_service_manager_(fixed_service_manager), link_manager_(link_manager),
       signalling_manager_(l2cap_handler_, this, &data_pipeline_manager_, dynamic_service_manager_,
                           &dynamic_channel_allocator_, fixed_service_manager_) {
   ASSERT(l2cap_handler_ != nullptr);
@@ -44,19 +51,67 @@
   ASSERT(parameter_provider_ != nullptr);
   link_idle_disconnect_alarm_.Schedule(common::BindOnce(&Link::Disconnect, common::Unretained(this)),
                                        parameter_provider_->GetClassicLinkIdleDisconnectTimeout());
+  acl_connection_->RegisterCallbacks(this, l2cap_handler_);
 }
 
 void Link::OnAclDisconnected(hci::ErrorCode status) {
+  signalling_manager_.CancelAlarm();
   fixed_channel_allocator_.OnAclDisconnected(status);
   dynamic_channel_allocator_.OnAclDisconnected(status);
+  ConnectionResult result{
+      .connection_result_code = ConnectionResultCode::FAIL_HCI_ERROR,
+      .hci_error = status,
+      .l2cap_connection_response_result = ConnectionResponseResult::SUCCESS,
+  };
+  while (!local_cid_to_pending_dynamic_channel_connection_map_.empty()) {
+    auto entry = local_cid_to_pending_dynamic_channel_connection_map_.begin();
+    NotifyChannelFail(entry->first, result);
+  }
 }
 
 void Link::Disconnect() {
   acl_connection_->Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
 }
 
-std::shared_ptr<FixedChannelImpl> Link::AllocateFixedChannel(Cid cid, SecurityPolicy security_policy) {
-  auto channel = fixed_channel_allocator_.AllocateChannel(cid, security_policy);
+void Link::Encrypt() {
+  acl_connection_->SetConnectionEncryption(hci::Enable::ENABLED);
+}
+
+void Link::Authenticate() {
+  acl_connection_->AuthenticationRequested();
+}
+
+bool Link::IsAuthenticated() const {
+  return encryption_enabled_ != hci::EncryptionEnabled::OFF;
+}
+
+void Link::ReadRemoteVersionInformation() {
+  acl_connection_->ReadRemoteVersionInformation();
+}
+
+void Link::ReadRemoteSupportedFeatures() {
+  acl_connection_->ReadRemoteSupportedFeatures();
+}
+
+void Link::ReadRemoteExtendedFeatures() {
+  acl_connection_->ReadRemoteExtendedFeatures();
+}
+
+void Link::ReadClockOffset() {
+  acl_connection_->ReadClockOffset();
+}
+
+void Link::AcquireSecurityHold() {
+  used_by_security_module_ = true;
+  RefreshRefCount();
+}
+void Link::ReleaseSecurityHold() {
+  used_by_security_module_ = false;
+  RefreshRefCount();
+}
+
+std::shared_ptr<FixedChannelImpl> Link::AllocateFixedChannel(Cid cid) {
+  auto channel = fixed_channel_allocator_.AllocateChannel(cid);
   data_pipeline_manager_.AttachChannel(cid, channel, l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
   return channel;
 }
@@ -75,15 +130,70 @@
 
 void Link::SendConnectionRequest(Psm psm, Cid local_cid,
                                  PendingDynamicChannelConnection pending_dynamic_channel_connection) {
-  local_cid_to_pending_dynamic_channel_connection_map_[local_cid] = std::move(pending_dynamic_channel_connection);
-  signalling_manager_.SendConnectionRequest(psm, local_cid);
+  if (pending_dynamic_channel_connection.configuration_.channel_mode ==
+          RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION &&
+      !remote_extended_feature_received_) {
+    pending_dynamic_psm_list_.push_back(psm);
+    pending_dynamic_channel_callback_list_.push_back(std::move(pending_dynamic_channel_connection));
+    LOG_INFO("Will connect after information response ERTM feature support is received");
+    dynamic_channel_allocator_.FreeChannel(local_cid);
+    return;
+  } else if (pending_dynamic_channel_connection.configuration_.channel_mode ==
+                 RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION &&
+             !GetRemoteSupportsErtm()) {
+    LOG_WARN("Remote doesn't support ERTM. Dropping connection request");
+    ConnectionResult result{
+        .connection_result_code = ConnectionResultCode::FAIL_REMOTE_NOT_SUPPORT,
+    };
+    pending_dynamic_channel_connection.on_fail_callback_.Invoke(result);
+    dynamic_channel_allocator_.FreeChannel(local_cid);
+    return;
+  } else {
+    local_cid_to_pending_dynamic_channel_connection_map_[local_cid] = std::move(pending_dynamic_channel_connection);
+    signalling_manager_.SendConnectionRequest(psm, local_cid);
+  }
 }
 
-void Link::OnOutgoingConnectionRequestFail(Cid local_cid) {
-  local_cid_to_pending_dynamic_channel_connection_map_.erase(local_cid);
+void Link::SetPendingDynamicChannels(std::list<Psm> psm_list,
+                                     std::list<Link::PendingDynamicChannelConnection> callback_list) {
+  ASSERT(psm_list.size() == callback_list.size());
+  pending_dynamic_psm_list_ = std::move(psm_list);
+  pending_dynamic_channel_callback_list_ = std::move(callback_list);
+}
+
+void Link::connect_to_pending_dynamic_channels() {
+  auto psm = pending_dynamic_psm_list_.begin();
+  auto callback = pending_dynamic_channel_callback_list_.begin();
+  while (psm != pending_dynamic_psm_list_.end()) {
+    SendConnectionRequest(*psm, ReserveDynamicChannel(), std::move(*callback));
+    psm++;
+    callback++;
+  }
+}
+
+void Link::send_pending_configuration_requests() {
+  for (auto local_cid : pending_outgoing_configuration_request_list_) {
+    signalling_manager_.SendInitialConfigRequest(local_cid);
+  }
+  pending_outgoing_configuration_request_list_.clear();
+}
+
+void Link::OnOutgoingConnectionRequestFail(Cid local_cid, ConnectionResult result) {
+  if (local_cid_to_pending_dynamic_channel_connection_map_.find(local_cid) !=
+      local_cid_to_pending_dynamic_channel_connection_map_.end()) {
+    NotifyChannelFail(local_cid, result);
+  }
   dynamic_channel_allocator_.FreeChannel(local_cid);
 }
 
+void Link::SendInitialConfigRequestOrQueue(Cid local_cid) {
+  if (remote_extended_feature_received_) {
+    signalling_manager_.SendInitialConfigRequest(local_cid);
+  } else {
+    pending_outgoing_configuration_request_list_.push_back(local_cid);
+  }
+}
+
 void Link::SendDisconnectionRequest(Cid local_cid, Cid remote_cid) {
   signalling_manager_.SendDisconnectionRequest(local_cid, remote_cid);
 }
@@ -92,24 +202,19 @@
   signalling_manager_.SendInformationRequest(type);
 }
 
-std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateDynamicChannel(Psm psm, Cid remote_cid,
-                                                                                  SecurityPolicy security_policy) {
-  auto channel = dynamic_channel_allocator_.AllocateChannel(psm, remote_cid, security_policy);
+std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateDynamicChannel(Psm psm, Cid remote_cid) {
+  auto channel = dynamic_channel_allocator_.AllocateChannel(psm, remote_cid);
   if (channel != nullptr) {
-    data_pipeline_manager_.AttachChannel(channel->GetCid(), channel,
-                                         l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
     RefreshRefCount();
   }
   channel->local_initiated_ = false;
   return channel;
 }
 
-std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateReservedDynamicChannel(
-    Cid reserved_cid, Psm psm, Cid remote_cid, SecurityPolicy security_policy) {
-  auto channel = dynamic_channel_allocator_.AllocateReservedChannel(reserved_cid, psm, remote_cid, security_policy);
+std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateReservedDynamicChannel(Cid reserved_cid, Psm psm,
+                                                                                          Cid remote_cid) {
+  auto channel = dynamic_channel_allocator_.AllocateReservedChannel(reserved_cid, psm, remote_cid);
   if (channel != nullptr) {
-    data_pipeline_manager_.AttachChannel(channel->GetCid(), channel,
-                                         l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
     RefreshRefCount();
   }
   channel->local_initiated_ = true;
@@ -126,7 +231,6 @@
   if (dynamic_channel_allocator_.FindChannelByCid(cid) == nullptr) {
     return;
   }
-  data_pipeline_manager_.DetachChannel(cid);
   dynamic_channel_allocator_.FreeChannel(cid);
   RefreshRefCount();
 }
@@ -135,6 +239,9 @@
   int ref_count = 0;
   ref_count += fixed_channel_allocator_.GetRefCount();
   ref_count += dynamic_channel_allocator_.NumberOfChannels();
+  if (used_by_security_module_) {
+    ref_count += 1;
+  }
   ASSERT_LOG(ref_count >= 0, "ref_count %d is less than 0", ref_count);
   if (ref_count > 0) {
     link_idle_disconnect_alarm_.Cancel();
@@ -148,19 +255,15 @@
   ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
          local_cid_to_pending_dynamic_channel_connection_map_.end());
   auto& pending_dynamic_channel_connection = local_cid_to_pending_dynamic_channel_connection_map_[cid];
-  pending_dynamic_channel_connection.handler_->Post(
-      common::BindOnce(std::move(pending_dynamic_channel_connection.on_open_callback_), std::move(user_channel)));
+  pending_dynamic_channel_connection.on_open_callback_.Invoke(std::move(user_channel));
   local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
 }
 
-void Link::NotifyChannelFail(Cid cid) {
+void Link::NotifyChannelFail(Cid cid, ConnectionResult result) {
   ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
          local_cid_to_pending_dynamic_channel_connection_map_.end());
   auto& pending_dynamic_channel_connection = local_cid_to_pending_dynamic_channel_connection_map_[cid];
-  // TODO(cmanton) Pass proper connection falure result to user
-  DynamicChannelManager::ConnectionResult result;
-  pending_dynamic_channel_connection.handler_->Post(
-      common::BindOnce(std::move(pending_dynamic_channel_connection.on_fail_callback_), result));
+  pending_dynamic_channel_connection.on_fail_callback_.Invoke(result);
   local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
 }
 
@@ -172,22 +275,122 @@
   return remote_connectionless_mtu_;
 }
 
-void Link::SetRemoteSupportsErtm(bool supported) {
-  remote_supports_ertm_ = supported;
-}
-
 bool Link::GetRemoteSupportsErtm() const {
   return remote_supports_ertm_;
 }
 
-void Link::SetRemoteSupportsFcs(bool supported) {
-  remote_supports_fcs_ = supported;
-}
-
 bool Link::GetRemoteSupportsFcs() const {
   return remote_supports_fcs_;
 }
 
+void Link::OnRemoteExtendedFeatureReceived(bool ertm_supported, bool fcs_supported) {
+  remote_supports_ertm_ = ertm_supported;
+  remote_supports_fcs_ = fcs_supported;
+  remote_extended_feature_received_ = true;
+  connect_to_pending_dynamic_channels();
+  send_pending_configuration_requests();
+}
+
+void Link::AddChannelPendingingAuthentication(PendingAuthenticateDynamicChannelConnection pending_channel) {
+  pending_channel_list_.push_back(std::move(pending_channel));
+}
+
+void Link::OnConnectionPacketTypeChanged(uint16_t packet_type) {
+  LOG_DEBUG("UNIMPLEMENTED %s packet_type:%x", __func__, packet_type);
+}
+
+void Link::OnAuthenticationComplete() {
+  if (!pending_channel_list_.empty()) {
+    acl_connection_->SetConnectionEncryption(hci::Enable::ENABLED);
+  }
+}
+
+void Link::OnEncryptionChange(hci::EncryptionEnabled enabled) {
+  encryption_enabled_ = enabled;
+  if (encryption_enabled_ == hci::EncryptionEnabled::OFF) {
+    LOG_DEBUG("Encryption has changed to disabled");
+    return;
+  }
+  LOG_DEBUG("Encryption has changed to enabled .. restarting channels:%zd", pending_channel_list_.size());
+
+  for (auto& channel : pending_channel_list_) {
+    local_cid_to_pending_dynamic_channel_connection_map_[channel.cid_] =
+        std::move(channel.pending_dynamic_channel_connection_);
+    signalling_manager_.SendConnectionRequest(channel.psm_, channel.cid_);
+  }
+  pending_channel_list_.clear();
+}
+
+void Link::OnChangeConnectionLinkKeyComplete() {
+  LOG_DEBUG("UNIMPLEMENTED %s", __func__);
+}
+
+void Link::OnReadClockOffsetComplete(uint16_t clock_offset) {
+  LOG_DEBUG("UNIMPLEMENTED %s clock_offset:%d", __func__, clock_offset);
+}
+
+void Link::OnModeChange(hci::Mode current_mode, uint16_t interval) {
+  LOG_DEBUG("UNIMPLEMENTED %s mode:%s interval:%d", __func__, hci::ModeText(current_mode).c_str(), interval);
+}
+
+void Link::OnQosSetupComplete(hci::ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
+                              uint32_t latency, uint32_t delay_variation) {
+  LOG_DEBUG("UNIMPLEMENTED %s service_type:%s token_rate:%d peak_bandwidth:%d latency:%d delay_varitation:%d", __func__,
+            hci::ServiceTypeText(service_type).c_str(), token_rate, peak_bandwidth, latency, delay_variation);
+}
+void Link::OnFlowSpecificationComplete(hci::FlowDirection flow_direction, hci::ServiceType service_type,
+                                       uint32_t token_rate, uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                       uint32_t access_latency) {
+  LOG_DEBUG(
+      "UNIMPLEMENTED %s flow_direction:%s service_type:%s token_rate:%d token_bucket_size:%d peak_bandwidth:%d "
+      "access_latency:%d",
+      __func__, hci::FlowDirectionText(flow_direction).c_str(), hci::ServiceTypeText(service_type).c_str(), token_rate,
+      token_bucket_size, peak_bandwidth, access_latency);
+}
+void Link::OnFlushOccurred() {
+  LOG_DEBUG("UNIMPLEMENTED %s", __func__);
+}
+void Link::OnRoleDiscoveryComplete(hci::Role current_role) {
+  LOG_DEBUG("UNIMPLEMENTED %s current_role:%s", __func__, hci::RoleText(current_role).c_str());
+}
+void Link::OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) {
+  LOG_DEBUG("UNIMPLEMENTED %s link_policy_settings:0x%x", __func__, link_policy_settings);
+}
+void Link::OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) {
+  LOG_DEBUG("UNIMPLEMENTED %s flush_timeout:%d", __func__, flush_timeout);
+}
+void Link::OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) {
+  LOG_DEBUG("UNIMPLEMENTED %s transmit_power_level:%d", __func__, transmit_power_level);
+}
+void Link::OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) {
+  LOG_DEBUG("UNIMPLEMENTED %s link_supervision_timeout:%d", __func__, link_supervision_timeout);
+}
+void Link::OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) {
+  LOG_DEBUG("UNIMPLEMENTED %sfailed_contact_counter:%hu", __func__, failed_contact_counter);
+}
+void Link::OnReadLinkQualityComplete(uint8_t link_quality) {
+  LOG_DEBUG("UNIMPLEMENTED %s link_quality:%hhu", __func__, link_quality);
+}
+void Link::OnReadAfhChannelMapComplete(hci::AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) {
+  LOG_DEBUG("UNIMPLEMENTED %s afh_mode:%s", __func__, hci::AfhModeText(afh_mode).c_str());
+}
+void Link::OnReadRssiComplete(uint8_t rssi) {
+  LOG_DEBUG("UNIMPLEMENTED %s rssi:%hhd", __func__, rssi);
+}
+void Link::OnReadClockComplete(uint32_t clock, uint16_t accuracy) {
+  LOG_DEBUG("UNIMPLEMENTED %s clock:%u accuracy:%hu", __func__, clock, accuracy);
+}
+void Link::OnMasterLinkKeyComplete(hci::KeyFlag key_flag) {
+  LOG_DEBUG("UNIMPLEMENTED key_flag:%s", hci::KeyFlagText(key_flag).c_str());
+}
+void Link::OnRoleChange(hci::Role new_role) {
+  LOG_DEBUG("UNIMPLEMENTED role:%s", hci::RoleText(new_role).c_str());
+}
+void Link::OnDisconnection(hci::ErrorCode reason) {
+  OnAclDisconnected(reason);
+  link_manager_->OnDisconnect(GetDevice().GetAddress(), reason);
+}
+
 }  // namespace internal
 }  // namespace classic
 }  // namespace l2cap
diff --git a/gd/l2cap/classic/internal/link.h b/gd/l2cap/classic/internal/link.h
index b29d767..e33295b 100644
--- a/gd/l2cap/classic/internal/link.h
+++ b/gd/l2cap/classic/internal/link.h
@@ -19,11 +19,12 @@
 #include <memory>
 #include <unordered_map>
 
-#include "hci/acl_manager.h"
+#include "hci/acl_manager/classic_acl_connection.h"
 #include "l2cap/classic/dynamic_channel_configuration_option.h"
 #include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
 #include "l2cap/classic/internal/fixed_channel_impl.h"
 #include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/classic/security_enforcement_interface.h"
 #include "l2cap/internal/data_pipeline_manager.h"
 #include "l2cap/internal/dynamic_channel_allocator.h"
 #include "l2cap/internal/dynamic_channel_impl.h"
@@ -39,17 +40,17 @@
 namespace classic {
 namespace internal {
 
-class Link : public l2cap::internal::ILink {
- public:
-  Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
-       l2cap::internal::ParameterProvider* parameter_provider,
-       DynamicChannelServiceManagerImpl* dynamic_service_manager,
-       FixedChannelServiceManagerImpl* fixed_service_manager);
+class LinkManager;
 
-  ~Link() override = default;
+class Link : public l2cap::internal::ILink, public hci::acl_manager::ConnectionManagementCallbacks {
+ public:
+  Link(os::Handler* l2cap_handler, std::unique_ptr<hci::acl_manager::ClassicAclConnection> acl_connection,
+       l2cap::internal::ParameterProvider* parameter_provider,
+       DynamicChannelServiceManagerImpl* dynamic_service_manager, FixedChannelServiceManagerImpl* fixed_service_manager,
+       LinkManager* link_manager);
 
   hci::AddressWithType GetDevice() override {
-    return {acl_connection_->GetAddress(), acl_connection_->GetAddressType()};
+    return {acl_connection_->GetAddress(), hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   }
 
   struct PendingDynamicChannelConnection {
@@ -59,15 +60,41 @@
     classic::DynamicChannelConfigurationOption configuration_;
   };
 
+  struct PendingAuthenticateDynamicChannelConnection {
+    Psm psm_;
+    Cid cid_;
+    PendingDynamicChannelConnection pending_dynamic_channel_connection_;
+  };
+
   // ACL methods
 
   virtual void OnAclDisconnected(hci::ErrorCode status);
 
   virtual void Disconnect();
 
+  virtual void Encrypt();
+
+  virtual void Authenticate();
+
+  virtual bool IsAuthenticated() const;
+
+  virtual void ReadRemoteVersionInformation();
+
+  virtual void ReadRemoteSupportedFeatures();
+
+  virtual void ReadRemoteExtendedFeatures();
+
+  virtual void ReadClockOffset();
+
+  // Increase the link usage refcount to ensure the link won't be disconnected when SecurityModule needs it
+  virtual void AcquireSecurityHold();
+
+  // Decrease the link usage refcount when SecurityModule no longer needs it
+  virtual void ReleaseSecurityHold();
+
   // FixedChannel methods
 
-  std::shared_ptr<FixedChannelImpl> AllocateFixedChannel(Cid cid, SecurityPolicy security_policy);
+  std::shared_ptr<FixedChannelImpl> AllocateFixedChannel(Cid cid);
 
   virtual bool IsFixedChannelAllocated(Cid cid);
 
@@ -79,18 +106,23 @@
   virtual void SendConnectionRequest(Psm psm, Cid local_cid,
                                      PendingDynamicChannelConnection pending_dynamic_channel_connection);
 
+  // When a Link is established, LinkManager notifies pending dynamic channels to connect
+  virtual void SetPendingDynamicChannels(std::list<Psm> psm_list,
+                                         std::list<Link::PendingDynamicChannelConnection> callback_list);
+
   // Invoked by signalling manager to indicate an outgoing connection request failed and link shall free resources
-  virtual void OnOutgoingConnectionRequestFail(Cid local_cid);
+  virtual void OnOutgoingConnectionRequestFail(Cid local_cid, DynamicChannelManager::ConnectionResult result);
+
+  virtual void SendInitialConfigRequestOrQueue(Cid local_cid);
 
   virtual void SendInformationRequest(InformationRequestInfoType type);
 
   virtual void SendDisconnectionRequest(Cid local_cid, Cid remote_cid) override;
 
-  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateDynamicChannel(Psm psm, Cid remote_cid,
-                                                                                      SecurityPolicy security_policy);
+  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateDynamicChannel(Psm psm, Cid remote_cid);
 
-  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateReservedDynamicChannel(
-      Cid reserved_cid, Psm psm, Cid remote_cid, SecurityPolicy security_policy);
+  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateReservedDynamicChannel(Cid reserved_cid, Psm psm,
+                                                                                              Cid remote_cid);
 
   virtual classic::DynamicChannelConfigurationOption GetConfigurationForInitialConfiguration(Cid cid);
 
@@ -100,15 +132,14 @@
   virtual void RefreshRefCount();
 
   virtual void NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> channel);
-  virtual void NotifyChannelFail(Cid cid);
+  virtual void NotifyChannelFail(Cid cid, DynamicChannelManager::ConnectionResult result);
 
   // Information received from signaling channel
   virtual void SetRemoteConnectionlessMtu(Mtu mtu);
   virtual Mtu GetRemoteConnectionlessMtu() const;
-  virtual void SetRemoteSupportsErtm(bool supported);
   virtual bool GetRemoteSupportsErtm() const;
-  virtual void SetRemoteSupportsFcs(bool supported);
   virtual bool GetRemoteSupportsFcs() const;
+  virtual void OnRemoteExtendedFeatureReceived(bool ertm_supported, bool fcs_supported);
 
   virtual std::string ToString() {
     return GetDevice().ToString();
@@ -116,21 +147,61 @@
 
   void SendLeCredit(Cid local_cid, uint16_t credit) override {}
 
+  void AddChannelPendingingAuthentication(PendingAuthenticateDynamicChannelConnection pending_channel);
+
+  // ConnectionManagementCallbacks
+  void OnConnectionPacketTypeChanged(uint16_t packet_type) override;
+  void OnAuthenticationComplete() override;
+  void OnEncryptionChange(hci::EncryptionEnabled enabled) override;
+  void OnChangeConnectionLinkKeyComplete() override;
+  void OnReadClockOffsetComplete(uint16_t clock_offset) override;
+  void OnModeChange(hci::Mode current_mode, uint16_t interval) override;
+  void OnQosSetupComplete(hci::ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
+                          uint32_t delay_variation) override;
+  void OnFlowSpecificationComplete(hci::FlowDirection flow_direction, hci::ServiceType service_type,
+                                   uint32_t token_rate, uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                   uint32_t access_latency) override;
+  void OnFlushOccurred() override;
+  void OnRoleDiscoveryComplete(hci::Role current_role) override;
+  void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) override;
+  void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) override;
+  void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) override;
+  void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) override;
+  void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) override;
+  void OnReadLinkQualityComplete(uint8_t link_quality) override;
+  void OnReadAfhChannelMapComplete(hci::AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) override;
+  void OnReadRssiComplete(uint8_t rssi) override;
+  void OnReadClockComplete(uint32_t clock, uint16_t accuracy) override;
+  void OnMasterLinkKeyComplete(hci::KeyFlag key_flag) override;
+  void OnRoleChange(hci::Role new_role) override;
+  void OnDisconnection(hci::ErrorCode reason) override;
+
  private:
+  void connect_to_pending_dynamic_channels();
+  void send_pending_configuration_requests();
+
   os::Handler* l2cap_handler_;
   l2cap::internal::FixedChannelAllocator<FixedChannelImpl, Link> fixed_channel_allocator_{this, l2cap_handler_};
   l2cap::internal::DynamicChannelAllocator dynamic_channel_allocator_{this, l2cap_handler_};
-  std::unique_ptr<hci::AclConnection> acl_connection_;
+  std::unique_ptr<hci::acl_manager::ClassicAclConnection> acl_connection_;
   l2cap::internal::DataPipelineManager data_pipeline_manager_;
   l2cap::internal::ParameterProvider* parameter_provider_;
   DynamicChannelServiceManagerImpl* dynamic_service_manager_;
   FixedChannelServiceManagerImpl* fixed_service_manager_;
-  ClassicSignallingManager signalling_manager_;
+  LinkManager* link_manager_;
   std::unordered_map<Cid, PendingDynamicChannelConnection> local_cid_to_pending_dynamic_channel_connection_map_;
   os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
+  ClassicSignallingManager signalling_manager_;
   Mtu remote_connectionless_mtu_ = kMinimumClassicMtu;
+  bool remote_extended_feature_received_ = false;
   bool remote_supports_ertm_ = false;
   bool remote_supports_fcs_ = false;
+  hci::EncryptionEnabled encryption_enabled_ = hci::EncryptionEnabled::OFF;
+  std::list<Link::PendingAuthenticateDynamicChannelConnection> pending_channel_list_;
+  std::list<Psm> pending_dynamic_psm_list_;
+  std::list<Link::PendingDynamicChannelConnection> pending_dynamic_channel_callback_list_;
+  std::list<uint16_t> pending_outgoing_configuration_request_list_;
+  bool used_by_security_module_ = false;
   DISALLOW_COPY_AND_ASSIGN(Link);
 };
 
diff --git a/gd/l2cap/classic/internal/link_manager.cc b/gd/l2cap/classic/internal/link_manager.cc
index 96e1333..737fbb1 100644
--- a/gd/l2cap/classic/internal/link_manager.cc
+++ b/gd/l2cap/classic/internal/link_manager.cc
@@ -16,7 +16,7 @@
 #include <memory>
 #include <unordered_map>
 
-#include "hci/acl_manager.h"
+#include "hci/acl_manager/classic_acl_connection.h"
 #include "hci/address.h"
 #include "l2cap/classic/internal/link.h"
 #include "l2cap/internal/scheduler_fifo.h"
@@ -53,7 +53,7 @@
         continue;
       }
       // Allocate channel for newly registered fixed channels
-      auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+      auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first);
       fixed_channel_service.second->NotifyChannelCreation(
           std::make_unique<FixedChannel>(fixed_channel_impl, l2cap_handler_));
       num_new_channels++;
@@ -95,9 +95,30 @@
     }
     return;
   }
+  if (dynamic_channel_service_manager_->GetService(psm)->GetSecurityPolicy() !=
+          SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK &&
+      !link->IsAuthenticated()) {
+    link->AddChannelPendingingAuthentication(
+        {psm, link->ReserveDynamicChannel(), std::move(pending_dynamic_channel_connection)});
+    link->Authenticate();
+    return;
+  }
   link->SendConnectionRequest(psm, link->ReserveDynamicChannel(), std::move(pending_dynamic_channel_connection));
 }
 
+void LinkManager::InitiateConnectionForSecurity(hci::Address remote) {
+  auto* link = GetLink(remote);
+  if (link != nullptr) {
+    LOG_ERROR("Link already exists for %s", remote.ToString().c_str());
+  }
+  acl_manager_->CreateConnection(remote);
+}
+
+void LinkManager::RegisterLinkSecurityInterfaceListener(os::Handler* handler, LinkSecurityInterfaceListener* listener) {
+  link_security_interface_listener_handler_ = handler;
+  link_security_interface_listener_ = listener;
+}
+
 Link* LinkManager::GetLink(const hci::Address device) {
   if (links_.find(device) == links_.end()) {
     return nullptr;
@@ -105,16 +126,85 @@
   return &links_.find(device)->second;
 }
 
-void LinkManager::OnConnectSuccess(std::unique_ptr<hci::AclConnection> acl_connection) {
+void LinkManager::handle_link_security_hold(hci::Address remote) {
+  auto link = GetLink(remote);
+  if (link == nullptr) {
+    LOG_WARN("Remote is disconnected");
+    return;
+  }
+  link->AcquireSecurityHold();
+}
+
+void LinkManager::handle_link_security_release(hci::Address remote) {
+  auto link = GetLink(remote);
+  if (link == nullptr) {
+    LOG_WARN("Remote is disconnected");
+    return;
+  }
+  link->ReleaseSecurityHold();
+}
+
+void LinkManager::handle_link_security_disconnect(hci::Address remote) {
+  auto link = GetLink(remote);
+  if (link == nullptr) {
+    LOG_WARN("Remote is disconnected");
+    return;
+  }
+  link->Disconnect();
+}
+
+void LinkManager::handle_link_security_ensure_authenticated(hci::Address remote) {
+  auto link = GetLink(remote);
+  if (link == nullptr) {
+    LOG_WARN("Remote is disconnected");
+    return;
+  }
+  if (!link->IsAuthenticated()) {
+    link->Authenticate();
+  }
+}
+
+/**
+ * The implementation for LinkSecurityInterface, which allows the SecurityModule to access some link functionalities.
+ * Note: All public methods implementing this interface are invoked from external context.
+ */
+class LinkSecurityInterfaceImpl : public LinkSecurityInterface {
+ public:
+  LinkSecurityInterfaceImpl(os::Handler* handler, LinkManager* link_manager, Link* link)
+      : handler_(handler), link_manager_(link_manager), remote_(link->GetDevice().GetAddress()) {}
+
+  hci::Address GetRemoteAddress() override {
+    return remote_;
+  }
+
+  void Hold() override {
+    handler_->CallOn(link_manager_, &LinkManager::handle_link_security_hold, remote_);
+  }
+
+  void Release() override {
+    handler_->CallOn(link_manager_, &LinkManager::handle_link_security_release, remote_);
+  }
+
+  void Disconnect() override {
+    handler_->CallOn(link_manager_, &LinkManager::handle_link_security_disconnect, remote_);
+  }
+
+  void EnsureAuthenticated() override {
+    handler_->CallOn(link_manager_, &LinkManager::handle_link_security_ensure_authenticated, remote_);
+  }
+
+  os::Handler* handler_;
+  LinkManager* link_manager_;
+  hci::Address remote_;
+};
+
+void LinkManager::OnConnectSuccess(std::unique_ptr<hci::acl_manager::ClassicAclConnection> acl_connection) {
   // Same link should not be connected twice
   hci::Address device = acl_connection->GetAddress();
   ASSERT_LOG(GetLink(device) == nullptr, "%s is connected twice without disconnection",
              acl_connection->GetAddress().ToString().c_str());
-  // Register ACL disconnection callback in LinkManager so that we can clean up link resource properly
-  acl_connection->RegisterDisconnectCallback(
-      common::BindOnce(&LinkManager::OnDisconnect, common::Unretained(this), device), l2cap_handler_);
   links_.try_emplace(device, l2cap_handler_, std::move(acl_connection), parameter_provider_,
-                     dynamic_channel_service_manager_, fixed_channel_service_manager_);
+                     dynamic_channel_service_manager_, fixed_channel_service_manager_, this);
   auto* link = GetLink(device);
   ASSERT(link != nullptr);
   link->SendInformationRequest(InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED);
@@ -123,27 +213,27 @@
   // Allocate and distribute channels for all registered fixed channel services
   auto fixed_channel_services = fixed_channel_service_manager_->GetRegisteredServices();
   for (auto& fixed_channel_service : fixed_channel_services) {
-    auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+    auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first);
     fixed_channel_service.second->NotifyChannelCreation(
         std::make_unique<FixedChannel>(fixed_channel_impl, l2cap_handler_));
   }
   if (pending_dynamic_channels_.find(device) != pending_dynamic_channels_.end()) {
-    for (Psm psm : pending_dynamic_channels_[device]) {
-      auto& callbacks = pending_dynamic_channels_callbacks_[device].front();
-      link->SendConnectionRequest(psm, link->ReserveDynamicChannel(), std::move(callbacks));
-      pending_dynamic_channels_callbacks_[device].pop_front();
-    }
+    auto psm_list = pending_dynamic_channels_[device];
+    auto& callback_list = pending_dynamic_channels_callbacks_[device];
+    link->SetPendingDynamicChannels(psm_list, std::move(callback_list));
     pending_dynamic_channels_.erase(device);
     pending_dynamic_channels_callbacks_.erase(device);
   }
-  // Remove device from pending links list, if any
-  auto pending_link = pending_links_.find(device);
-  if (pending_link == pending_links_.end()) {
-    // This an incoming connection, exit
-    return;
+  // Notify security manager
+  if (link_security_interface_listener_handler_ != nullptr) {
+    link_security_interface_listener_handler_->CallOn(
+        link_security_interface_listener_,
+        &LinkSecurityInterfaceListener::OnLinkConnected,
+        std::make_unique<LinkSecurityInterfaceImpl>(l2cap_handler_, this, link));
   }
-  // This is an outgoing connection, remove entry in pending link list
-  pending_links_.erase(pending_link);
+
+  // Remove device from pending links list, if any
+  pending_links_.erase(device);
 }
 
 void LinkManager::OnConnectFail(hci::Address device, hci::ErrorCode reason) {
@@ -151,13 +241,13 @@
   auto pending_link = pending_links_.find(device);
   if (pending_link == pending_links_.end()) {
     // There is no pending link, exit
-    LOG_DEBUG("Connection to %s failed without a pending link", device.ToString().c_str());
+    LOG_DEBUG("Connection to %s failed without a pending link; reason: %s", device.ToString().c_str(),
+              hci::ErrorCodeText(reason).c_str());
     if (pending_dynamic_channels_callbacks_.find(device) != pending_dynamic_channels_callbacks_.end()) {
       for (Link::PendingDynamicChannelConnection& callbacks : pending_dynamic_channels_callbacks_[device]) {
-        callbacks.handler_->Post(common::BindOnce(std::move(callbacks.on_fail_callback_),
-                                                  DynamicChannelManager::ConnectionResult{
-                                                      .hci_error = hci::ErrorCode::CONNECTION_TIMEOUT,
-                                                  }));
+        callbacks.on_fail_callback_.Invoke(DynamicChannelManager::ConnectionResult{
+            .hci_error = hci::ErrorCode::CONNECTION_TIMEOUT,
+        });
       }
       pending_dynamic_channels_.erase(device);
       pending_dynamic_channels_callbacks_.erase(device);
@@ -178,8 +268,11 @@
   auto* link = GetLink(device);
   ASSERT_LOG(link != nullptr, "Device %s is disconnected with reason 0x%x, but not in local database",
              device.ToString().c_str(), static_cast<uint8_t>(status));
-  link->OnAclDisconnected(status);
   links_.erase(device);
+  if (link_security_interface_listener_handler_ != nullptr) {
+    link_security_interface_listener_handler_->CallOn(
+        link_security_interface_listener_, &LinkSecurityInterfaceListener::OnLinkDisconnected, device);
+  }
 }
 
 }  // namespace internal
diff --git a/gd/l2cap/classic/internal/link_manager.h b/gd/l2cap/classic/internal/link_manager.h
index 6d282a2..ae6f897 100644
--- a/gd/l2cap/classic/internal/link_manager.h
+++ b/gd/l2cap/classic/internal/link_manager.h
@@ -19,13 +19,14 @@
 #include <memory>
 #include <unordered_map>
 
-#include "hci/acl_manager.h"
+#include "hci/acl_manager/classic_acl_connection.h"
 #include "hci/address.h"
 #include "l2cap/classic/dynamic_channel_manager.h"
 #include "l2cap/classic/fixed_channel_manager.h"
 #include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
 #include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
 #include "l2cap/classic/internal/link.h"
+#include "l2cap/classic/link_security_interface.h"
 #include "l2cap/internal/parameter_provider.h"
 #include "l2cap/internal/scheduler.h"
 #include "os/handler.h"
@@ -35,7 +36,7 @@
 namespace classic {
 namespace internal {
 
-class LinkManager : public hci::ConnectionCallbacks {
+class LinkManager : public hci::acl_manager::ConnectionCallbacks {
  public:
   LinkManager(os::Handler* l2cap_handler, hci::AclManager* acl_manager,
               FixedChannelServiceManagerImpl* fixed_channel_service_manager,
@@ -59,9 +60,9 @@
   // ACL methods
 
   Link* GetLink(hci::Address device);
-  void OnConnectSuccess(std::unique_ptr<hci::AclConnection> acl_connection) override;
+  void OnConnectSuccess(std::unique_ptr<hci::acl_manager::ClassicAclConnection> acl_connection) override;
   void OnConnectFail(hci::Address device, hci::ErrorCode reason) override;
-  void OnDisconnect(hci::Address device, hci::ErrorCode status);
+  virtual void OnDisconnect(hci::Address device, hci::ErrorCode status);
 
   // FixedChannelManager methods
 
@@ -69,10 +70,23 @@
 
   // DynamicChannelManager methods
 
-  void ConnectDynamicChannelServices(hci::Address device,
-                                     Link::PendingDynamicChannelConnection pending_dynamic_channel_connection, Psm psm);
+  void ConnectDynamicChannelServices(
+      hci::Address device, Link::PendingDynamicChannelConnection pending_connection, Psm psm);
+
+  // For SecurityModule to initiate an ACL link
+  void InitiateConnectionForSecurity(hci::Address remote);
+
+  // LinkManager will handle sending OnLinkConnected() callback and construct a LinkSecurityInterface proxy.
+  void RegisterLinkSecurityInterfaceListener(os::Handler* handler, LinkSecurityInterfaceListener* listener);
 
  private:
+  // Handles requests from LinkSecurityInterface
+  friend class LinkSecurityInterfaceImpl;
+  void handle_link_security_hold(hci::Address remote);
+  void handle_link_security_release(hci::Address remote);
+  void handle_link_security_disconnect(hci::Address remote);
+  void handle_link_security_ensure_authenticated(hci::Address remote);
+
   // Dependencies
   os::Handler* l2cap_handler_;
   hci::AclManager* acl_manager_;
@@ -86,6 +100,8 @@
   std::unordered_map<hci::Address, std::list<Psm>> pending_dynamic_channels_;
   std::unordered_map<hci::Address, std::list<Link::PendingDynamicChannelConnection>>
       pending_dynamic_channels_callbacks_;
+  os::Handler* link_security_interface_listener_handler_ = nullptr;
+  LinkSecurityInterfaceListener* link_security_interface_listener_ = nullptr;
   DISALLOW_COPY_AND_ASSIGN(LinkManager);
 };
 
diff --git a/gd/l2cap/classic/internal/link_manager_test.cc b/gd/l2cap/classic/internal/link_manager_test.cc
index aa7bfa4..d59e0dd 100644
--- a/gd/l2cap/classic/internal/link_manager_test.cc
+++ b/gd/l2cap/classic/internal/link_manager_test.cc
@@ -26,6 +26,7 @@
 #include "hci/address.h"
 #include "l2cap/cid.h"
 #include "l2cap/classic/fixed_channel_manager.h"
+#include "l2cap/classic/internal/dynamic_channel_service_impl_mock.h"
 #include "l2cap/classic/internal/fixed_channel_service_impl_mock.h"
 #include "l2cap/classic/internal/fixed_channel_service_manager_impl_mock.h"
 #include "l2cap/internal/parameter_provider_mock.h"
@@ -39,13 +40,15 @@
 namespace l2cap {
 namespace classic {
 namespace internal {
+namespace {
 
-using hci::testing::MockAclConnection;
 using hci::testing::MockAclManager;
+using hci::testing::MockClassicAclConnection;
 using l2cap::internal::testing::MockParameterProvider;
 using ::testing::_;  // Matcher to any value
 using ::testing::ByMove;
 using ::testing::DoAll;
+using testing::MockDynamicChannelServiceImpl;
 using testing::MockDynamicChannelServiceManagerImpl;
 using testing::MockFixedChannelServiceImpl;
 using testing::MockFixedChannelServiceManagerImpl;
@@ -53,7 +56,7 @@
 using ::testing::SaveArg;
 
 constexpr static auto kTestIdleDisconnectTimeoutLong = std::chrono::milliseconds(1000);
-constexpr static auto kTestIdleDisconnectTimeoutShort = std::chrono::milliseconds(30);
+constexpr static auto kTestIdleDisconnectTimeoutShort = std::chrono::milliseconds(1000);
 
 class L2capClassicLinkManagerTest : public ::testing::Test {
  public:
@@ -88,15 +91,23 @@
 TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl) {
   MockFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager;
   MockDynamicChannelServiceManagerImpl mock_classic_dynamic_channel_service_manager;
+  SecurityEnforcementRejectAllImpl security_module_impl;
+  MockDynamicChannelServiceImpl service;
+  ON_CALL(service, GetSecurityPolicy())
+      .WillByDefault(Return(SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK));
+
+  ON_CALL(mock_classic_dynamic_channel_service_manager, GetSecurityEnforcementInterface())
+      .WillByDefault(Return(&security_module_impl));
   MockAclManager mock_acl_manager;
   hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
   auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  hci::acl_manager::ConnectionCallbacks* hci_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  ON_CALL(mock_classic_dynamic_channel_service_manager, GetService(_)).WillByDefault(Return(&service));
   LinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager, &mock_classic_fixed_channel_service_manager,
                                    &mock_classic_dynamic_channel_service_manager, mock_parameter_provider_);
   EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
@@ -118,20 +129,31 @@
 
   // Step 3: ACL connection success event should trigger channel creation for all registered services
 
-  std::unique_ptr<MockAclConnection> acl_connection = std::make_unique<MockAclConnection>();
-  EXPECT_CALL(*acl_connection, RegisterDisconnectCallback(_, l2cap_handler_)).Times(1);
+  std::unique_ptr<MockClassicAclConnection> acl_connection = std::make_unique<MockClassicAclConnection>();
   EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
   std::unique_ptr<FixedChannel> channel_1, channel_2;
-  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).WillOnce([&channel_1](std::unique_ptr<FixedChannel> channel) {
-    channel_1 = std::move(channel);
-  });
-  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_)).WillOnce([&channel_2](std::unique_ptr<FixedChannel> channel) {
-    channel_2 = std::move(channel);
-  });
-  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::ConnectionCallbacks::OnConnectSuccess,
                                               common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
   SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
   EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
   EXPECT_NE(channel_2, nullptr);
 
   // Step 4: Calling ConnectServices() to the same device will no trigger another connection attempt
@@ -171,7 +193,7 @@
   auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  hci::acl_manager::ConnectionCallbacks* hci_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
@@ -205,7 +227,7 @@
   auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  hci::acl_manager::ConnectionCallbacks* hci_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
@@ -231,7 +253,7 @@
 
   // Step 3: ACL connection failure event should trigger connection failure callback
   EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).Times(0);
-  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectFail,
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::ConnectionCallbacks::OnConnectFail,
                                               common::Unretained(hci_connection_callbacks), device,
                                               hci::ErrorCode::PAGE_TIMEOUT));
   SyncHandler(hci_callback_handler);
@@ -251,7 +273,7 @@
   auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  hci::acl_manager::ConnectionCallbacks* hci_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
@@ -275,20 +297,32 @@
   classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
 
   // Step 3: ACL connection success event should trigger channel creation for all registered services
-  auto* raw_acl_connection = new MockAclConnection();
-  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  auto* raw_acl_connection = new MockClassicAclConnection();
+  std::unique_ptr<MockClassicAclConnection> acl_connection(raw_acl_connection);
   EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
   std::unique_ptr<FixedChannel> channel_1, channel_2;
-  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).WillOnce([&channel_1](std::unique_ptr<FixedChannel> channel) {
-    channel_1 = std::move(channel);
-  });
-  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_)).WillOnce([&channel_2](std::unique_ptr<FixedChannel> channel) {
-    channel_2 = std::move(channel);
-  });
-  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::ConnectionCallbacks::OnConnectSuccess,
                                               common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
   SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
   EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
   EXPECT_NE(channel_2, nullptr);
   hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
   channel_1->RegisterOnCloseCallback(
@@ -301,12 +335,6 @@
   EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(1);
   std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
 
-  // Step 5: Link disconnect will trigger all callbacks
-  classic_link_manager.OnDisconnect(device, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
-  SyncHandler(user_handler.get());
-  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
-  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
-
   user_handler->Clear();
 }
 
@@ -319,7 +347,7 @@
   auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  hci::acl_manager::ConnectionCallbacks* hci_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
@@ -343,20 +371,32 @@
   classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
 
   // Step 3: ACL connection success event should trigger channel creation for all registered services
-  auto* raw_acl_connection = new MockAclConnection();
-  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  auto* raw_acl_connection = new MockClassicAclConnection();
+  std::unique_ptr<MockClassicAclConnection> acl_connection(raw_acl_connection);
   EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
   std::unique_ptr<FixedChannel> channel_1, channel_2;
-  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).WillOnce([&channel_1](std::unique_ptr<FixedChannel> channel) {
-    channel_1 = std::move(channel);
-  });
-  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_)).WillOnce([&channel_2](std::unique_ptr<FixedChannel> channel) {
-    channel_2 = std::move(channel);
-  });
-  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::ConnectionCallbacks::OnConnectSuccess,
                                               common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
   SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
   EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
   EXPECT_NE(channel_2, nullptr);
   hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
   channel_1->RegisterOnCloseCallback(
@@ -371,12 +411,6 @@
   EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(0);
   std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 2);
 
-  // Step 5: Link disconnect will trigger all callbacks
-  classic_link_manager.OnDisconnect(device, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
-  SyncHandler(user_handler.get());
-  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
-  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
-
   user_handler->Clear();
 }
 
@@ -389,7 +423,7 @@
   auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  hci::acl_manager::ConnectionCallbacks* hci_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
@@ -413,20 +447,32 @@
   classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
 
   // Step 3: ACL connection success event should trigger channel creation for all registered services
-  auto* raw_acl_connection = new MockAclConnection();
-  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  auto* raw_acl_connection = new MockClassicAclConnection();
+  std::unique_ptr<MockClassicAclConnection> acl_connection(raw_acl_connection);
   EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
   std::unique_ptr<FixedChannel> channel_1, channel_2;
-  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).WillOnce([&channel_1](std::unique_ptr<FixedChannel> channel) {
-    channel_1 = std::move(channel);
-  });
-  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_)).WillOnce([&channel_2](std::unique_ptr<FixedChannel> channel) {
-    channel_2 = std::move(channel);
-  });
-  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::ConnectionCallbacks::OnConnectSuccess,
                                               common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
   SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
   EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
   EXPECT_NE(channel_2, nullptr);
   hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
   channel_1->RegisterOnCloseCallback(
@@ -446,15 +492,10 @@
   EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(1);
   std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
 
-  // Step 6: Link disconnect will trigger all callbacks
-  classic_link_manager.OnDisconnect(device, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
-  SyncHandler(user_handler.get());
-  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
-  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
-
   user_handler->Clear();
 }
 
+}  // namespace
 }  // namespace internal
 }  // namespace classic
 }  // namespace l2cap
diff --git a/gd/l2cap/classic/internal/link_mock.h b/gd/l2cap/classic/internal/link_mock.h
index 4eb336e..7bd02a1 100644
--- a/gd/l2cap/classic/internal/link_mock.h
+++ b/gd/l2cap/classic/internal/link_mock.h
@@ -29,20 +29,21 @@
 namespace internal {
 namespace testing {
 
-using hci::testing::MockAclConnection;
+using hci::testing::MockClassicAclConnection;
 
 class MockLink : public Link {
  public:
   explicit MockLink(os::Handler* handler, l2cap::internal::ParameterProvider* parameter_provider)
-      : Link(handler, std::make_unique<MockAclConnection>(), parameter_provider, nullptr, nullptr){};
+      : Link(handler, std::make_unique<MockClassicAclConnection>(), parameter_provider, nullptr, nullptr, nullptr){};
   explicit MockLink(os::Handler* handler, l2cap::internal::ParameterProvider* parameter_provider,
-                    std::unique_ptr<hci::AclConnection> acl_connection)
-      : Link(handler, std::move(acl_connection), parameter_provider, nullptr, nullptr){};
+                    std::unique_ptr<hci::acl_manager::ClassicAclConnection> acl_connection, LinkManager* link_manager)
+      : Link(handler, std::move(acl_connection), parameter_provider, nullptr, nullptr, nullptr){};
   MOCK_METHOD(hci::AddressWithType, GetDevice, (), (override));
   MOCK_METHOD(void, OnAclDisconnected, (hci::ErrorCode status), (override));
   MOCK_METHOD(void, Disconnect, (), (override));
-  MOCK_METHOD(std::shared_ptr<l2cap::internal::DynamicChannelImpl>, AllocateDynamicChannel,
-              (Psm psm, Cid cid, SecurityPolicy security_policy), (override));
+  MOCK_METHOD(std::shared_ptr<l2cap::internal::DynamicChannelImpl>, AllocateDynamicChannel, (Psm psm, Cid cid),
+              (override));
+
   MOCK_METHOD(bool, IsFixedChannelAllocated, (Cid cid), (override));
   MOCK_METHOD(void, RefreshRefCount, (), (override));
 };
@@ -51,4 +52,4 @@
 }  // namespace internal
 }  // namespace classic
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/link_test.cc b/gd/l2cap/classic/internal/link_test.cc
new file mode 100644
index 0000000..6e09151
--- /dev/null
+++ b/gd/l2cap/classic/internal/link_test.cc
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+#include "l2cap/classic/internal/link.h"
+
+#include "hci/acl_manager_mock.h"
+#include "hci/address.h"
+#include "l2cap/classic/internal/dynamic_channel_service_impl_mock.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+
+#include <gmock/gmock-nice-strict.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using ::testing::NiceMock;
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace {
+
+constexpr Psm kPsm = 123;
+constexpr Cid kCid = 456;
+
+using classic::internal::testing::MockDynamicChannelServiceImpl;
+using classic::internal::testing::MockDynamicChannelServiceManagerImpl;
+using hci::testing::MockClassicAclConnection;
+using l2cap::internal::testing::MockParameterProvider;
+using testing::MockFixedChannelServiceManagerImpl;
+
+class L2capClassicLinkTest : public ::testing::Test {
+ public:
+  void OnOpen(std::unique_ptr<DynamicChannel> channel) {
+    on_open_promise_.set_value();
+  }
+
+  void OnFail(DynamicChannelManager::ConnectionResult result) {
+    on_fail_promise_.set_value();
+  }
+
+  void OnDequeueCallbackForTest() {
+    std::unique_ptr<BasePacketBuilder> data = raw_acl_connection_->acl_queue_.GetDownEnd()->TryDequeue();
+    if (data != nullptr) {
+      dequeue_promise_.set_value();
+    }
+  }
+
+  void EnqueueCallbackForTest() {
+    raw_acl_connection_->acl_queue_.GetDownEnd()->RegisterDequeue(
+        handler_, common::Bind(&L2capClassicLinkTest::OnDequeueCallbackForTest, common::Unretained(this)));
+  }
+
+  void DequeueCallback() {
+    raw_acl_connection_->acl_queue_.GetDownEnd()->UnregisterDequeue();
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    handler_ = new os::Handler(thread_);
+    signalling_handler_ = new os::Handler(thread_);
+
+    raw_acl_connection_ = new NiceMock<MockClassicAclConnection>();
+    link_ = new Link(signalling_handler_, std::unique_ptr<MockClassicAclConnection>(raw_acl_connection_),
+                     &mock_parameter_provider_, &mock_classic_dynamic_channel_service_manager_,
+                     &mock_classic_fixed_channel_service_manager_, nullptr);
+  }
+
+  void TearDown() override {
+    delete link_;
+
+    signalling_handler_->Clear();
+    delete signalling_handler_;
+
+    handler_->Clear();
+    delete handler_;
+
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* handler_ = nullptr;
+  os::Handler* signalling_handler_ = nullptr;
+
+  MockClassicAclConnection* raw_acl_connection_ = nullptr;
+  std::unique_ptr<MockClassicAclConnection> acl_connection_;
+
+  NiceMock<MockParameterProvider> mock_parameter_provider_;
+  MockFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager_;
+  MockDynamicChannelServiceManagerImpl mock_classic_dynamic_channel_service_manager_;
+  SecurityEnforcementRejectAllImpl security_module_impl_;
+
+  std::promise<void> on_open_promise_;
+  std::promise<void> on_fail_promise_;
+  std::promise<void> dequeue_promise_;
+
+  Link* link_;
+};
+
+TEST_F(L2capClassicLinkTest, pending_channels_get_notified_on_acl_disconnect) {
+  EnqueueCallbackForTest();
+
+  Link::PendingDynamicChannelConnection pending_dynamic_channel_connection{
+      .on_open_callback_ = handler_->BindOn(this, &L2capClassicLinkTest::OnOpen),
+      .on_fail_callback_ = handler_->BindOnceOn(this, &L2capClassicLinkTest::OnFail),
+      .configuration_ = DynamicChannelConfigurationOption(),
+  };
+  auto future = on_fail_promise_.get_future();
+
+  MockDynamicChannelServiceImpl service;
+  ON_CALL(service, GetSecurityPolicy())
+      .WillByDefault(::testing::Return(SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK));
+
+  EXPECT_CALL(mock_classic_dynamic_channel_service_manager_, GetSecurityEnforcementInterface())
+      .WillOnce(::testing::Return(&security_module_impl_));
+  EXPECT_CALL(mock_classic_dynamic_channel_service_manager_, GetService(::testing::_))
+      .WillOnce(::testing::Return(&service));
+
+  link_->SendConnectionRequest(kPsm, kCid, std::move(pending_dynamic_channel_connection));
+  link_->OnAclDisconnected(hci::ErrorCode::UNKNOWN_HCI_COMMAND);
+  future.wait();
+
+  auto dequeue_future = dequeue_promise_.get_future();
+  dequeue_future.wait();
+  DequeueCallback();
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/signalling_manager.cc b/gd/l2cap/classic/internal/signalling_manager.cc
index b3686e4..65f27e5 100644
--- a/gd/l2cap/classic/internal/signalling_manager.cc
+++ b/gd/l2cap/classic/internal/signalling_manager.cc
@@ -32,6 +32,22 @@
 namespace internal {
 static constexpr auto kTimeout = std::chrono::seconds(3);
 
+static std::vector<ControlView> GetCommandsFromPacketView(PacketView<kLittleEndian> packet) {
+  size_t curr = 0;
+  size_t end = packet.size();
+  std::vector<ControlView> result;
+  while (curr < end) {
+    auto sub_view = packet.GetLittleEndianSubview(curr, end);
+    auto control = ControlView::Create(sub_view);
+    if (!control.IsValid()) {
+      return {};
+    }
+    result.push_back(control);
+    curr += 1 + 1 + 2 + control.GetPayload().size();
+  }
+  return result;
+}
+
 ClassicSignallingManager::ClassicSignallingManager(os::Handler* handler, Link* link,
                                                    l2cap::internal::DataPipelineManager* data_pipeline_manager,
                                                    DynamicChannelServiceManagerImpl* dynamic_service_manager,
@@ -42,7 +58,7 @@
       fixed_service_manager_(fixed_service_manager), alarm_(handler) {
   ASSERT(handler_ != nullptr);
   ASSERT(link_ != nullptr);
-  signalling_channel_ = link_->AllocateFixedChannel(kClassicSignallingCid, {});
+  signalling_channel_ = link_->AllocateFixedChannel(kClassicSignallingCid);
   signalling_channel_->GetQueueUpEnd()->RegisterDequeue(
       handler_, common::Bind(&ClassicSignallingManager::on_incoming_packet, common::Unretained(this)));
   enqueue_buffer_ =
@@ -50,17 +66,22 @@
 }
 
 ClassicSignallingManager::~ClassicSignallingManager() {
-  enqueue_buffer_.reset();
+  alarm_.Cancel();
   signalling_channel_->GetQueueUpEnd()->UnregisterDequeue();
   signalling_channel_ = nullptr;
+  enqueue_buffer_->Clear();
+  enqueue_buffer_.reset();
 }
 
 void ClassicSignallingManager::OnCommandReject(CommandRejectView command_reject_view) {
-  if (command_just_sent_.signal_id_ != command_reject_view.GetIdentifier() ||
-      command_just_sent_.command_code_ != command_reject_view.GetCode()) {
+  if (command_just_sent_.signal_id_ != command_reject_view.GetIdentifier()) {
     LOG_WARN("Unexpected command reject: no pending request");
     return;
   }
+  if (command_just_sent_.command_code_ == CommandCode::INFORMATION_REQUEST &&
+      command_just_sent_.info_type_ == InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED) {
+    link_->OnRemoteExtendedFeatureReceived(false, false);
+  }
   alarm_.Cancel();
   handle_send_next_command();
 
@@ -68,6 +89,24 @@
 }
 
 void ClassicSignallingManager::SendConnectionRequest(Psm psm, Cid local_cid) {
+  dynamic_service_manager_->GetSecurityEnforcementInterface()->Enforce(
+      link_->GetDevice(),
+      dynamic_service_manager_->GetService(psm)->GetSecurityPolicy(),
+      handler_->BindOnceOn(this, &ClassicSignallingManager::on_security_result_for_outgoing, psm, local_cid));
+}
+
+void ClassicSignallingManager::on_security_result_for_outgoing(Psm psm, Cid local_cid, bool result) {
+  if (!result) {
+    LOG_WARN("Security requirement can't be satisfied. Dropping connection request");
+    DynamicChannelManager::ConnectionResult connection_result{
+        .connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_SECURITY_BLOCK,
+        .hci_error = hci::ErrorCode::SUCCESS,
+        .l2cap_connection_response_result = ConnectionResponseResult::NO_RESOURCES_AVAILABLE,
+    };
+    link_->OnOutgoingConnectionRequestFail(local_cid, connection_result);
+    return;
+  }
+
   PendingCommand pending_command = {next_signal_id_, CommandCode::CONNECTION_REQUEST, psm, local_cid, {}, {}, {}};
   next_signal_id_++;
   pending_commands_.push(std::move(pending_command));
@@ -76,8 +115,8 @@
   }
 }
 
-void ClassicSignallingManager::SendConfigurationRequest(Cid remote_cid,
-                                                        std::vector<std::unique_ptr<ConfigurationOption>> config) {
+void ClassicSignallingManager::send_configuration_request(Cid remote_cid,
+                                                          std::vector<std::unique_ptr<ConfigurationOption>> config) {
   PendingCommand pending_command = {next_signal_id_,  CommandCode::CONFIGURATION_REQUEST, {}, {}, remote_cid, {},
                                     std::move(config)};
   next_signal_id_++;
@@ -110,6 +149,10 @@
   LOG_WARN("Not supported");
 }
 
+void ClassicSignallingManager::CancelAlarm() {
+  alarm_.Cancel();
+}
+
 void ClassicSignallingManager::OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid) {
   if (!IsPsmValid(psm)) {
     LOG_WARN("Invalid psm received from remote psm:%d remote_cid:%d", psm, remote_cid);
@@ -138,57 +181,43 @@
     return;
   }
 
-  auto new_channel = link_->AllocateDynamicChannel(psm, remote_cid, {});
+  dynamic_service_manager_->GetSecurityEnforcementInterface()->Enforce(
+      link_->GetDevice(),
+      dynamic_service_manager_->GetService(psm)->GetSecurityPolicy(),
+      handler_->BindOnceOn(
+          this, &ClassicSignallingManager::on_security_result_for_incoming, psm, remote_cid, signal_id));
+}
+
+void ClassicSignallingManager::on_security_result_for_incoming(
+    Psm psm, Cid remote_cid, SignalId signal_id, bool result) {
+  if (!result) {
+    send_connection_response(
+        signal_id,
+        remote_cid,
+        0,
+        ConnectionResponseResult::SECURITY_BLOCK,
+        ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
+    DynamicChannelManager::ConnectionResult connection_result{
+        .connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_SECURITY_BLOCK,
+        .hci_error = hci::ErrorCode::SUCCESS,
+        .l2cap_connection_response_result = ConnectionResponseResult::NO_RESOURCES_AVAILABLE,
+    };
+    link_->OnOutgoingConnectionRequestFail(0, connection_result);
+  }
+
+  auto new_channel = link_->AllocateDynamicChannel(psm, remote_cid);
   if (new_channel == nullptr) {
     LOG_WARN("Can't allocate dynamic channel");
     return;
   }
-  send_connection_response(signal_id, remote_cid, new_channel->GetCid(), ConnectionResponseResult::SUCCESS,
-                           ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
-  auto& configuration_state = channel_configuration_[new_channel->GetCid()];
-  auto* service = dynamic_service_manager_->GetService(psm);
-  auto initial_config = service->GetConfigOption();
+  send_connection_response(
+      signal_id,
+      remote_cid,
+      new_channel->GetCid(),
+      ConnectionResponseResult::SUCCESS,
+      ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
 
-  auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
-  mtu_configuration->mtu_ = initial_config.incoming_mtu;
-  configuration_state.incoming_mtu_ = initial_config.incoming_mtu;
-
-  auto fcs_option = std::make_unique<FrameCheckSequenceOption>();
-  fcs_option->fcs_type_ = FcsType::DEFAULT;
-  if (!link_->GetRemoteSupportsFcs()) {
-    fcs_option->fcs_type_ = FcsType::NO_FCS;
-    configuration_state.fcs_type_ = FcsType::NO_FCS;
-  }
-
-  auto retransmission_flow_control_configuration = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
-  if (!link_->GetRemoteSupportsErtm()) {
-    initial_config.channel_mode = DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
-  }
-  switch (initial_config.channel_mode) {
-    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC:
-      retransmission_flow_control_configuration->mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
-      configuration_state.retransmission_and_flow_control_mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
-      break;
-    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION:
-      retransmission_flow_control_configuration->mode_ =
-          RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
-      configuration_state.retransmission_and_flow_control_mode_ =
-          RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
-      // TODO: Decide where to put initial values
-      retransmission_flow_control_configuration->tx_window_size_ = 10;
-      retransmission_flow_control_configuration->max_transmit_ = 20;
-      retransmission_flow_control_configuration->retransmission_time_out_ = 2000;
-      retransmission_flow_control_configuration->monitor_time_out_ = 12000;
-      retransmission_flow_control_configuration->maximum_pdu_size_ = 1010;
-      break;
-  }
-  configuration_state.local_retransmission_and_flow_control_ = *retransmission_flow_control_configuration;
-
-  std::vector<std::unique_ptr<ConfigurationOption>> config;
-  config.emplace_back(std::move(mtu_configuration));
-  config.emplace_back(std::move(retransmission_flow_control_configuration));
-  config.emplace_back(std::move(fcs_option));
-  SendConfigurationRequest(remote_cid, std::move(config));
+  link_->SendInitialConfigRequestOrQueue(new_channel->GetCid());
 }
 
 void ClassicSignallingManager::OnConnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid,
@@ -205,46 +234,160 @@
     handle_send_next_command();
     return;
   }
+  if (result == ConnectionResponseResult::PENDING) {
+    alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
+                    kTimeout);
+    return;
+  }
+
   command_just_sent_.signal_id_ = kInvalidSignalId;
   alarm_.Cancel();
   if (result != ConnectionResponseResult::SUCCESS) {
-    link_->OnOutgoingConnectionRequestFail(cid);
+    DynamicChannelManager::ConnectionResult connection_result{
+        .connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR,
+        .hci_error = hci::ErrorCode::SUCCESS,
+        .l2cap_connection_response_result = result,
+    };
+    link_->OnOutgoingConnectionRequestFail(cid, connection_result);
     handle_send_next_command();
     return;
   }
   Psm pending_psm = command_just_sent_.psm_;
-  auto new_channel = link_->AllocateReservedDynamicChannel(cid, pending_psm, remote_cid, {});
+  auto new_channel = link_->AllocateReservedDynamicChannel(cid, pending_psm, remote_cid);
   if (new_channel == nullptr) {
     LOG_WARN("Can't allocate dynamic channel");
-    link_->OnOutgoingConnectionRequestFail(cid);
+    DynamicChannelManager::ConnectionResult connection_result{
+        .connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR,
+        .hci_error = hci::ErrorCode::SUCCESS,
+        .l2cap_connection_response_result = ConnectionResponseResult::NO_RESOURCES_AVAILABLE,
+    };
+    link_->OnOutgoingConnectionRequestFail(cid, connection_result);
     handle_send_next_command();
     return;
   }
 
-  auto& configuration_state = channel_configuration_[new_channel->GetCid()];
-  auto initial_config = link_->GetConfigurationForInitialConfiguration(new_channel->GetCid());
+  link_->SendInitialConfigRequestOrQueue(cid);
+}
+
+void ClassicSignallingManager::OnConfigurationRequest(SignalId signal_id, Cid cid, Continuation is_continuation,
+                                                      std::vector<std::unique_ptr<ConfigurationOption>> options) {
+  auto channel = channel_allocator_->FindChannelByCid(cid);
+  if (channel == nullptr) {
+    LOG_WARN("Configuration request for an unknown channel");
+    return;
+  }
+
+  auto& configuration_state = channel_configuration_[cid];
+  std::vector<std::unique_ptr<ConfigurationOption>> rsp_options;
+  ConfigurationResponseResult result = ConfigurationResponseResult::SUCCESS;
+  auto remote_rfc_mode = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
+
+  for (auto& option : options) {
+    switch (option->type_) {
+      case ConfigurationOptionType::MTU: {
+        auto* config = MtuConfigurationOption::Specialize(option.get());
+        if (config->mtu_ < kMinimumClassicMtu) {
+          LOG_WARN("Configuration request with Invalid MTU");
+          config->mtu_ = kDefaultClassicMtu;
+          rsp_options.emplace_back(std::make_unique<MtuConfigurationOption>(*config));
+          result = ConfigurationResponseResult::UNACCEPTABLE_PARAMETERS;
+        }
+        configuration_state.outgoing_mtu_ = config->mtu_;
+        break;
+      }
+      case ConfigurationOptionType::FLUSH_TIMEOUT: {
+        // TODO: Handle this configuration option
+        break;
+      }
+      case ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL: {
+        auto* config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
+        remote_rfc_mode = config->mode_;
+        if (config->mode_ == RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION) {
+          if (config->retransmission_time_out_ == 0) {
+            config->retransmission_time_out_ = 2000;
+          }
+          if (config->monitor_time_out_ == 0) {
+            config->monitor_time_out_ = 12000;
+          }
+        }
+        configuration_state.remote_retransmission_and_flow_control_ = *config;
+        configuration_state.retransmission_and_flow_control_mode_ = config->mode_;
+        rsp_options.emplace_back(std::make_unique<RetransmissionAndFlowControlConfigurationOption>(*config));
+        break;
+      }
+      case ConfigurationOptionType::FRAME_CHECK_SEQUENCE: {
+        // We determine whether to use FCS or not when we send config request
+        break;
+      }
+      default:
+        if (option->is_hint_ != ConfigurationOptionIsHint::OPTION_IS_A_HINT) {
+          LOG_WARN("Received some unsupported configuration option: %d", static_cast<int>(option->type_));
+          auto response =
+              ConfigurationResponseBuilder::Create(signal_id.Value(), channel->GetRemoteCid(), is_continuation,
+                                                   ConfigurationResponseResult::UNKNOWN_OPTIONS, {});
+          enqueue_buffer_->Enqueue(std::move(response), handler_);
+          return;
+        }
+        break;
+    }
+  }
+
+  auto initial_config_option = dynamic_service_manager_->GetService(channel->GetPsm())->GetConfigOption();
+
+  if (remote_rfc_mode == RetransmissionAndFlowControlModeOption::L2CAP_BASIC &&
+      initial_config_option.channel_mode ==
+          DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION) {
+    LOG_WARN("ERTM mandatory not allow mode configuration, disconnect channel.");
+    SendDisconnectionRequest(channel->GetCid(), channel->GetRemoteCid());
+    return;
+  }
+
+  if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ) {
+    std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(channel, handler_);
+    if (channel->local_initiated_) {
+      link_->NotifyChannelCreation(cid, std::move(user_channel));
+    } else {
+      dynamic_service_manager_->GetService(channel->GetPsm())->NotifyChannelCreation(std::move(user_channel));
+    }
+    configuration_state.state_ = ChannelConfigurationState::State::CONFIGURED;
+    data_pipeline_manager_->AttachChannel(cid, channel, l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
+    data_pipeline_manager_->UpdateClassicConfiguration(cid, configuration_state);
+  } else if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ_RSP) {
+    configuration_state.state_ = ChannelConfigurationState::State::WAIT_CONFIG_RSP;
+  }
+
+  auto response = ConfigurationResponseBuilder::Create(signal_id.Value(), channel->GetRemoteCid(), is_continuation,
+                                                       result, std::move(rsp_options));
+  enqueue_buffer_->Enqueue(std::move(response), handler_);
+}
+
+void ClassicSignallingManager::SendInitialConfigRequest(Cid local_cid) {
+  auto channel = channel_allocator_->FindChannelByCid(local_cid);
+  auto psm = channel->GetPsm();
+  auto& configuration_state = channel_configuration_[local_cid];
+  auto* service = dynamic_service_manager_->GetService(psm);
+  auto initial_config = service->GetConfigOption();
 
   auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
   mtu_configuration->mtu_ = initial_config.incoming_mtu;
   configuration_state.incoming_mtu_ = initial_config.incoming_mtu;
 
   auto fcs_option = std::make_unique<FrameCheckSequenceOption>();
-  fcs_option->fcs_type_ = FcsType::DEFAULT;
-  if (!link_->GetRemoteSupportsFcs()) {
-    fcs_option->fcs_type_ = FcsType::NO_FCS;
-    configuration_state.fcs_type_ = FcsType::NO_FCS;
+  fcs_option->fcs_type_ = FcsType::NO_FCS;
+  configuration_state.fcs_type_ = FcsType::NO_FCS;
+  if (link_->GetRemoteSupportsFcs()) {
+    fcs_option->fcs_type_ = FcsType::DEFAULT;
+    configuration_state.fcs_type_ = FcsType::DEFAULT;
   }
 
   auto retransmission_flow_control_configuration = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
-  if (!link_->GetRemoteSupportsErtm()) {
-    initial_config.channel_mode = DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
-  }
   switch (initial_config.channel_mode) {
     case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC:
       retransmission_flow_control_configuration->mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
       configuration_state.retransmission_and_flow_control_mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
       break;
     case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION:
+    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION_OPTIONAL:
       retransmission_flow_control_configuration->mode_ =
           RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
       configuration_state.retransmission_and_flow_control_mode_ =
@@ -261,67 +404,72 @@
 
   std::vector<std::unique_ptr<ConfigurationOption>> config;
   config.emplace_back(std::move(mtu_configuration));
-  config.emplace_back(std::move(retransmission_flow_control_configuration));
-  config.emplace_back(std::move(fcs_option));
-  SendConfigurationRequest(remote_cid, {});
+  if (initial_config.channel_mode != DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC) {
+    config.emplace_back(std::move(retransmission_flow_control_configuration));
+    config.emplace_back(std::move(fcs_option));
+  }
+  send_configuration_request(channel->GetRemoteCid(), std::move(config));
 }
 
-void ClassicSignallingManager::OnConfigurationRequest(SignalId signal_id, Cid cid, Continuation is_continuation,
-                                                      std::vector<std::unique_ptr<ConfigurationOption>> options) {
+void ClassicSignallingManager::negotiate_configuration(Cid cid, Continuation is_continuation,
+                                                       std::vector<std::unique_ptr<ConfigurationOption>> options) {
   auto channel = channel_allocator_->FindChannelByCid(cid);
-  if (channel == nullptr) {
-    LOG_WARN("Configuration request for an unknown channel");
-    return;
-  }
-
-  auto& configuration_state = channel_configuration_[cid];
-
+  auto& configuration_state = channel_configuration_[channel->GetCid()];
+  std::vector<std::unique_ptr<ConfigurationOption>> negotiation_config;
+  bool can_negotiate = false;
   for (auto& option : options) {
     switch (option->type_) {
       case ConfigurationOptionType::MTU: {
-        configuration_state.outgoing_mtu_ = MtuConfigurationOption::Specialize(option.get())->mtu_;
-        // TODO: If less than minimum (required by spec), reject
+        // MTU is non-negotiable option. Use default mtu size
+        auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
+        mtu_configuration->mtu_ = kDefaultClassicMtu;
+        configuration_state.incoming_mtu_ = kDefaultClassicMtu;
+        negotiation_config.emplace_back(std::move(mtu_configuration));
+        can_negotiate = true;
         break;
       }
+      case ConfigurationOptionType::FRAME_CHECK_SEQUENCE:
       case ConfigurationOptionType::FLUSH_TIMEOUT: {
-        // TODO: Handle this configuration option
+        // TODO: Handle these two configuration options negotiation.
+        can_negotiate = true;
         break;
       }
       case ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL: {
-        auto config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
-        configuration_state.remote_retransmission_and_flow_control_ = *config;
-        break;
-      }
-      case ConfigurationOptionType::FRAME_CHECK_SEQUENCE: {
-        configuration_state.fcs_type_ = FrameCheckSequenceOption::Specialize(option.get())->fcs_type_;
+        auto* config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
+        if (config->mode_ == RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION) {
+          configuration_state.retransmission_and_flow_control_mode_ = config->mode_;
+          configuration_state.local_retransmission_and_flow_control_ = *config;
+          negotiation_config.emplace_back(std::make_unique<RetransmissionAndFlowControlConfigurationOption>(*config));
+        } else if (config->mode_ == RetransmissionAndFlowControlModeOption::L2CAP_BASIC) {
+          auto initial_config_option = dynamic_service_manager_->GetService(channel->GetPsm())->GetConfigOption();
+          if (initial_config_option.channel_mode ==
+              DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION) {
+            // ERTM mandatory is not allow negotiating of retransmission and flow control mode, disconnect channel
+            SendDisconnectionRequest(channel->GetCid(), channel->GetRemoteCid());
+            return;
+          } else if (initial_config_option.channel_mode ==
+                     DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::
+                         ENHANCED_RETRANSMISSION_OPTIONAL) {
+            can_negotiate = true;
+            negotiation_config.emplace_back(std::make_unique<RetransmissionAndFlowControlConfigurationOption>(*config));
+          }
+        } else {
+          // Not support other retransmission and flow control mode, disconnect channel.
+          SendDisconnectionRequest(channel->GetCid(), channel->GetRemoteCid());
+          return;
+        }
         break;
       }
       default:
         LOG_WARN("Received some unsupported configuration option: %d", static_cast<int>(option->type_));
-        auto response =
-            ConfigurationResponseBuilder::Create(signal_id.Value(), channel->GetRemoteCid(), is_continuation,
-                                                 ConfigurationResponseResult::UNKNOWN_OPTIONS, {});
-        enqueue_buffer_->Enqueue(std::move(response), handler_);
         return;
     }
   }
-
-  if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ) {
-    std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(channel, handler_);
-    if (channel->local_initiated_) {
-      link_->NotifyChannelCreation(cid, std::move(user_channel));
-    } else {
-      dynamic_service_manager_->GetService(channel->GetPsm())->NotifyChannelCreation(std::move(user_channel));
-    }
-    configuration_state.state_ = ChannelConfigurationState::State::CONFIGURED;
-    data_pipeline_manager_->UpdateClassicConfiguration(cid, configuration_state);
-  } else if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ_RSP) {
-    configuration_state.state_ = ChannelConfigurationState::State::WAIT_CONFIG_RSP;
+  if (can_negotiate) {
+    send_configuration_request(channel->GetRemoteCid(), std::move(negotiation_config));
+  } else {
+    LOG_DEBUG("No suggested parameter received");
   }
-
-  auto response = ConfigurationResponseBuilder::Create(signal_id.Value(), channel->GetRemoteCid(), is_continuation,
-                                                       ConfigurationResponseResult::SUCCESS, {});
-  enqueue_buffer_->Enqueue(std::move(response), handler_);
 }
 
 void ClassicSignallingManager::OnConfigurationResponse(SignalId signal_id, Cid cid, Continuation is_continuation,
@@ -342,8 +490,31 @@
     return;
   }
 
-  // TODO: Handle status not SUCCESS
+  switch (result) {
+    default:
+    case ConfigurationResponseResult::REJECTED:
+    case ConfigurationResponseResult::UNKNOWN_OPTIONS:
+    case ConfigurationResponseResult::FLOW_SPEC_REJECTED:
+      LOG_WARN("Configuration response not SUCCESS: %s", ConfigurationResponseResultText(result).c_str());
+      alarm_.Cancel();
+      handle_send_next_command();
+      return;
 
+    case ConfigurationResponseResult::PENDING:
+      alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
+                      kTimeout);
+      return;
+
+    case ConfigurationResponseResult::UNACCEPTABLE_PARAMETERS:
+      LOG_INFO("Configuration response with unacceptable parameters");
+      alarm_.Cancel();
+      negotiate_configuration(cid, is_continuation, std::move(options));
+      handle_send_next_command();
+      return;
+
+    case ConfigurationResponseResult::SUCCESS:
+      break;
+  }
   auto& configuration_state = channel_configuration_[channel->GetCid()];
 
   for (auto& option : options) {
@@ -359,7 +530,12 @@
       }
       case ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL: {
         auto config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
-        configuration_state.retransmission_and_flow_control_mode_ = config->mode_;
+        if (configuration_state.retransmission_and_flow_control_mode_ != config->mode_) {
+          SendDisconnectionRequest(cid, channel->GetRemoteCid());
+          alarm_.Cancel();
+          handle_send_next_command();
+          return;
+        }
         configuration_state.local_retransmission_and_flow_control_ = *config;
         break;
       }
@@ -369,6 +545,8 @@
       }
       default:
         LOG_WARN("Received some unsupported configuration option: %d", static_cast<int>(option->type_));
+        alarm_.Cancel();
+        handle_send_next_command();
         return;
     }
   }
@@ -381,6 +559,7 @@
       dynamic_service_manager_->GetService(channel->GetPsm())->NotifyChannelCreation(std::move(user_channel));
     }
     configuration_state.state_ = ChannelConfigurationState::State::CONFIGURED;
+    data_pipeline_manager_->AttachChannel(cid, channel, l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
     data_pipeline_manager_->UpdateClassicConfiguration(cid, configuration_state);
   } else if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ_RSP) {
     configuration_state.state_ = ChannelConfigurationState::State::WAIT_CONFIG_REQ;
@@ -400,7 +579,12 @@
   auto builder = DisconnectionResponseBuilder::Create(signal_id.Value(), cid, remote_cid);
   enqueue_buffer_->Enqueue(std::move(builder), handler_);
   channel->OnClosed(hci::ErrorCode::SUCCESS);
+  auto& configuration_state = channel_configuration_[channel->GetCid()];
+  if (configuration_state.state_ == configuration_state.CONFIGURED) {
+    data_pipeline_manager_->DetachChannel(cid);
+  }
   link_->FreeDynamicChannel(cid);
+  channel_configuration_.erase(cid);
 }
 
 void ClassicSignallingManager::OnDisconnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid) {
@@ -422,8 +606,13 @@
   }
 
   channel->OnClosed(hci::ErrorCode::SUCCESS);
+  auto& configuration_state = channel_configuration_[cid];
+  if (configuration_state.state_ == configuration_state.CONFIGURED) {
+    data_pipeline_manager_->DetachChannel(cid);
+  }
   link_->FreeDynamicChannel(cid);
   handle_send_next_command();
+  channel_configuration_.erase(cid);
 }
 
 void ClassicSignallingManager::OnEchoRequest(SignalId signal_id, const PacketView<kLittleEndian>& packet) {
@@ -455,9 +644,9 @@
       break;
     }
     case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
-      // TODO: implement this response
       auto response = InformationResponseExtendedFeaturesBuilder::Create(
-          signal_id.Value(), InformationRequestResult::SUCCESS, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
+          signal_id.Value(), InformationRequestResult::SUCCESS, 0, 0, 0, 1 /* ERTM */, 0 /* Streaming mode */,
+          1 /* FCS */, 0, 1 /* Fixed Channels */, 0, 0);
       enqueue_buffer_->Enqueue(std::move(response), handler_);
       break;
     }
@@ -496,8 +685,7 @@
         LOG_WARN("Invalid InformationResponseExtendedFeatures received");
         return;
       }
-      link_->SetRemoteSupportsErtm((view.GetEnhancedRetransmissionMode()));
-      link_->SetRemoteSupportsFcs(view.GetFcsOption());
+      link_->OnRemoteExtendedFeatureReceived(view.GetEnhancedRetransmissionMode(), view.GetFcsOption());
       // We don't care about other parameters
       break;
     }
@@ -518,7 +706,13 @@
 
 void ClassicSignallingManager::on_incoming_packet() {
   auto packet = signalling_channel_->GetQueueUpEnd()->TryDequeue();
-  ControlView control_packet_view = ControlView::Create(*packet);
+  auto command_list = GetCommandsFromPacketView(*packet);
+  for (auto& command : command_list) {
+    handle_one_command(command);
+  }
+}
+
+void ClassicSignallingManager::handle_one_command(ControlView control_packet_view) {
   if (!control_packet_view.IsValid()) {
     LOG_WARN("Invalid signalling packet received");
     return;
@@ -643,15 +837,26 @@
     LOG_ERROR("No pending command");
     return;
   }
-
+  LOG_WARN("Response time out for %s", CommandCodeText(command_just_sent_.command_code_).c_str());
   switch (command_just_sent_.command_code_) {
     case CommandCode::CONNECTION_REQUEST: {
-      link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+      DynamicChannelManager::ConnectionResult connection_result{
+          .connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR,
+          .hci_error = hci::ErrorCode::SUCCESS,
+          .l2cap_connection_response_result = ConnectionResponseResult::NO_RESOURCES_AVAILABLE,
+      };
+      link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_, connection_result);
       break;
     }
     case CommandCode::CONFIGURATION_REQUEST: {
       auto channel = channel_allocator_->FindChannelByRemoteCid(command_just_sent_.destination_cid_);
       SendDisconnectionRequest(channel->GetCid(), channel->GetRemoteCid());
+      return;
+    }
+    case CommandCode::INFORMATION_REQUEST: {
+      if (command_just_sent_.info_type_ == InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED) {
+        link_->OnRemoteExtendedFeatureReceived(false, false);
+      }
       break;
     }
     default:
diff --git a/gd/l2cap/classic/internal/signalling_manager.h b/gd/l2cap/classic/internal/signalling_manager.h
index 0f706e4..e12f89d 100644
--- a/gd/l2cap/classic/internal/signalling_manager.h
+++ b/gd/l2cap/classic/internal/signalling_manager.h
@@ -66,7 +66,7 @@
 
   void SendConnectionRequest(Psm psm, Cid local_cid);
 
-  void SendConfigurationRequest(Cid remote_cid, std::vector<std::unique_ptr<ConfigurationOption>> config);
+  void SendInitialConfigRequest(Cid local_cid);
 
   void SendDisconnectionRequest(Cid local_cid, Cid remote_cid);
 
@@ -74,6 +74,8 @@
 
   void SendEchoRequest(std::unique_ptr<packet::RawBuilder> payload);
 
+  void CancelAlarm();
+
   void OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid);
 
   void OnConnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid, ConnectionResponseResult result,
@@ -99,11 +101,19 @@
 
  private:
   void on_incoming_packet();
+  void handle_one_command(ControlView control_view);
   void send_connection_response(SignalId signal_id, Cid remote_cid, Cid local_cid, ConnectionResponseResult result,
                                 ConnectionResponseStatus status);
   void on_command_timeout();
   void handle_send_next_command();
 
+  void negotiate_configuration(Cid cid, Continuation is_continuation,
+                               std::vector<std::unique_ptr<ConfigurationOption>>);
+
+  void send_configuration_request(Cid remote_cid, std::vector<std::unique_ptr<ConfigurationOption>> config);
+  void on_security_result_for_incoming(Psm psm, Cid remote_cid, SignalId signal_id, bool result);
+  void on_security_result_for_outgoing(Psm psm, Cid local_cid, bool result);
+
   os::Handler* handler_;
   Link* link_;
   l2cap::internal::DataPipelineManager* data_pipeline_manager_;
diff --git a/gd/l2cap/classic/l2cap_classic_module.cc b/gd/l2cap/classic/l2cap_classic_module.cc
index ca5a0d5..7dabed4 100644
--- a/gd/l2cap/classic/l2cap_classic_module.cc
+++ b/gd/l2cap/classic/l2cap_classic_module.cc
@@ -38,9 +38,13 @@
 
 const ModuleFactory L2capClassicModule::Factory = ModuleFactory([]() { return new L2capClassicModule(); });
 
+static SecurityEnforcementRejectAllImpl default_security_module_impl_;
+
 struct L2capClassicModule::impl {
   impl(os::Handler* l2cap_handler, hci::AclManager* acl_manager)
-      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager) {}
+      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager) {
+    dynamic_channel_service_manager_impl_.SetSecurityEnforcementInterface(&default_security_module_impl_);
+  }
   os::Handler* l2cap_handler_;
   hci::AclManager* acl_manager_;
   l2cap::internal::ParameterProvider parameter_provider_;
@@ -48,6 +52,29 @@
   internal::DynamicChannelServiceManagerImpl dynamic_channel_service_manager_impl_{l2cap_handler_};
   internal::LinkManager link_manager_{l2cap_handler_, acl_manager_, &fixed_channel_service_manager_impl_,
                                       &dynamic_channel_service_manager_impl_, &parameter_provider_};
+
+  struct SecurityInterfaceImpl : public SecurityInterface {
+    SecurityInterfaceImpl(impl* module_impl) : module_impl_(module_impl) {}
+
+    void RegisterLinkSecurityInterfaceListener(os::Handler* handler, LinkSecurityInterfaceListener* listener) {
+      ASSERT(!registered_);
+      module_impl_->link_manager_.RegisterLinkSecurityInterfaceListener(handler, listener);
+      registered_ = true;
+    }
+
+    void InitiateConnectionForSecurity(hci::Address remote) override {
+      ASSERT(registered_);
+      module_impl_->link_manager_.InitiateConnectionForSecurity(remote);
+    }
+
+    void Unregister() override {
+      ASSERT(registered_);
+      module_impl_->link_manager_.RegisterLinkSecurityInterfaceListener(nullptr, nullptr);
+      registered_ = false;
+    }
+    impl* module_impl_;
+    bool registered_ = false;
+  } security_interface_impl_{this};
 };
 
 L2capClassicModule::L2capClassicModule() {}
@@ -80,6 +107,21 @@
       &pimpl_->dynamic_channel_service_manager_impl_, &pimpl_->link_manager_, pimpl_->l2cap_handler_));
 }
 
+void L2capClassicModule::InjectSecurityEnforcementInterface(
+    SecurityEnforcementInterface* security_enforcement_interface) {
+  if (security_enforcement_interface != nullptr) {
+    pimpl_->dynamic_channel_service_manager_impl_.SetSecurityEnforcementInterface(security_enforcement_interface);
+  } else {
+    pimpl_->dynamic_channel_service_manager_impl_.SetSecurityEnforcementInterface(&default_security_module_impl_);
+  }
+}
+
+SecurityInterface* L2capClassicModule::GetSecurityInterface(
+    os::Handler* handler, LinkSecurityInterfaceListener* listener) {
+  pimpl_->security_interface_impl_.RegisterLinkSecurityInterfaceListener(handler, listener);
+  return &pimpl_->security_interface_impl_;
+}
+
 }  // namespace classic
 }  // namespace l2cap
 }  // namespace bluetooth
diff --git a/gd/l2cap/classic/l2cap_classic_module.h b/gd/l2cap/classic/l2cap_classic_module.h
index 388cae2..35ca64c 100644
--- a/gd/l2cap/classic/l2cap_classic_module.h
+++ b/gd/l2cap/classic/l2cap_classic_module.h
@@ -19,9 +19,16 @@
 
 #include "l2cap/classic/dynamic_channel_manager.h"
 #include "l2cap/classic/fixed_channel_manager.h"
+#include "l2cap/classic/link_security_interface.h"
+#include "l2cap/classic/security_enforcement_interface.h"
 #include "module.h"
 
 namespace bluetooth {
+
+namespace security {
+class SecurityModule;
+}
+
 namespace l2cap {
 namespace classic {
 
@@ -54,6 +61,23 @@
  private:
   struct impl;
   std::unique_ptr<impl> pimpl_;
+
+  friend security::SecurityModule;
+  /**
+   * Only for the classic security module to inject functionality to enforce security level for a connection. When
+   * classic security module is stopping, inject nullptr. Note: We expect this only to be called during stack startup.
+   * This is not synchronized.
+   */
+  virtual void InjectSecurityEnforcementInterface(SecurityEnforcementInterface* security_enforcement_interface);
+
+  /**
+   * Get the interface for Security Module to access link function.
+   * Security Module needs to register the callback for ACL link connected and disconnected. When connected, either by
+   * incoming or by outgoing connection request, Security Module receives a LinkSecurityInterface proxy, which can be
+   * used to access some link functionlities.
+   */
+  virtual SecurityInterface* GetSecurityInterface(os::Handler* handler, LinkSecurityInterfaceListener* listener);
+
   DISALLOW_COPY_AND_ASSIGN(L2capClassicModule);
 };
 
diff --git a/gd/l2cap/classic/link_security_interface.h b/gd/l2cap/classic/link_security_interface.h
new file mode 100644
index 0000000..27d511f
--- /dev/null
+++ b/gd/l2cap/classic/link_security_interface.h
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "hci/address.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+/**
+ * This is a proxy for Security Module to unregister itself, or to initiate link connection.
+ */
+class SecurityInterface {
+ public:
+  virtual ~SecurityInterface() = default;
+
+  /**
+   * Page a remote device for ACL connection, when Security Module needs it for pairing. When the remote device is
+   * connected, Security Module will receive a callback through LinkSecurityInterfaceListener.
+   */
+  virtual void InitiateConnectionForSecurity(hci::Address remote) = 0;
+
+  /**
+   * Unregister the security interface and the LinkSecurityInterfaceListener.
+   */
+  virtual void Unregister() = 0;
+};
+
+/**
+ * This is a proxy for Security Module to access some link function. This object is passed to Security Module when a
+ * link is established.
+ */
+class LinkSecurityInterface {
+ public:
+  virtual ~LinkSecurityInterface() = default;
+
+  virtual hci::Address GetRemoteAddress() = 0;
+
+  /**
+   * Hold the ACL link connection. Don't disconnect the link until Release() is called.
+   */
+  virtual void Hold() = 0;
+
+  /**
+   * Release the ACL link connection. This doesn't guarantee link disconnection, if other L2cap services are using the
+   * link.
+   */
+  virtual void Release() = 0;
+
+  /**
+   * Force the ACL link to disconnect.
+   */
+  virtual void Disconnect() = 0;
+
+  /**
+   * Initiate pairing to HCI layer.
+   */
+  virtual void EnsureAuthenticated() = 0;
+};
+
+class LinkSecurityInterfaceListener {
+ public:
+  virtual ~LinkSecurityInterfaceListener() = default;
+
+  /**
+   * Each time when an ACL link is connected, security manager receives this callback to use LinkSecurityInterface
+   * functions.
+   */
+  virtual void OnLinkConnected(std::unique_ptr<LinkSecurityInterface>) {}
+
+  /**
+   * When an ACL link is disconnected, security manager receives this callback. The corresponding LinkSecurityInterface
+   * is invalidated then.
+   * @param remote
+   */
+  virtual void OnLinkDisconnected(hci::Address remote) {}
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/security_enforcement_interface.h b/gd/l2cap/classic/security_enforcement_interface.h
new file mode 100644
index 0000000..01a5c7a
--- /dev/null
+++ b/gd/l2cap/classic/security_enforcement_interface.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "common/contextual_callback.h"
+#include "hci/address_with_type.h"
+#include "l2cap/classic/security_policy.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+/**
+ * The interface for Security Module to implement.
+ */
+class SecurityEnforcementInterface {
+ public:
+  virtual ~SecurityEnforcementInterface() = default;
+
+  using ResultCallback = common::ContextualOnceCallback<void(bool)>;
+
+  /**
+   * Invoked when L2CAP needs to open a channel with given security requirement. When the Security Module satisfies the
+   * required security level, or cannot satisfy at all, invoke the result_callback.
+   */
+  virtual void Enforce(hci::AddressWithType remote, SecurityPolicy policy, ResultCallback result_callback) = 0;
+};
+
+/**
+ * A default implementation which cannot satisfy any security level except
+ * _SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK.
+ */
+class SecurityEnforcementRejectAllImpl : public SecurityEnforcementInterface {
+ public:
+  void Enforce(hci::AddressWithType remote, SecurityPolicy policy, ResultCallback result_callback) override {
+    if (policy == SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK) {
+      result_callback.InvokeIfNotEmpty(true);
+    } else {
+      result_callback.InvokeIfNotEmpty(false);
+    }
+  }
+};
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/security_policy.h b/gd/l2cap/classic/security_policy.h
similarity index 62%
copy from gd/l2cap/security_policy.h
copy to gd/l2cap/classic/security_policy.h
index 5a06401..dde297f 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/l2cap/classic/security_policy.h
@@ -13,12 +13,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
+#include <cstdint>
+
 namespace bluetooth {
 namespace l2cap {
+namespace classic {
 
-class SecurityPolicy {};
+enum class SecurityPolicy {
+  // Predefined security policies for user to pick
 
+  // Just encryption, but no MITM
+  ENCRYPTED_TRANSPORT,
+
+  // Implicitly MITM protected
+  AUTHENTICATED_ENCRYPTED_TRANSPORT,
+
+  // Same as AUTHENTICATED_ENCRYPTED_TRANSPORT
+  BEST,
+
+  // No security enforced. SDP only.
+  _SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK,
+
+};
+
+}  // namespace classic
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/dynamic_channel.cc b/gd/l2cap/dynamic_channel.cc
index f8b64e6..19d527f 100644
--- a/gd/l2cap/dynamic_channel.cc
+++ b/gd/l2cap/dynamic_channel.cc
@@ -21,23 +21,26 @@
 namespace bluetooth {
 namespace l2cap {
 
-hci::Address DynamicChannel::GetDevice() const {
+hci::AddressWithType DynamicChannel::GetDevice() const {
   return impl_->GetDevice();
 }
 
-void DynamicChannel::RegisterOnCloseCallback(os::Handler* user_handler,
-                                             DynamicChannel::OnCloseCallback on_close_callback) {
-  l2cap_handler_->Post(common::BindOnce(&l2cap::internal::DynamicChannelImpl::RegisterOnCloseCallback, impl_,
-                                        user_handler, std::move(on_close_callback)));
+void DynamicChannel::RegisterOnCloseCallback(DynamicChannel::OnCloseCallback on_close_callback) {
+  l2cap_handler_->CallOn(
+      impl_.get(), &l2cap::internal::DynamicChannelImpl::RegisterOnCloseCallback, std::move(on_close_callback));
 }
 
 void DynamicChannel::Close() {
-  l2cap_handler_->Post(common::BindOnce(&l2cap::internal::DynamicChannelImpl::Close, impl_));
+  l2cap_handler_->CallOn(impl_.get(), &l2cap::internal::DynamicChannelImpl::Close);
 }
 
 common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>*
 DynamicChannel::GetQueueUpEnd() const {
   return impl_->GetQueueUpEnd();
 }
+
+Cid DynamicChannel::HACK_GetRemoteCid() {
+  return impl_->GetRemoteCid();
+}
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/dynamic_channel.h b/gd/l2cap/dynamic_channel.h
index b7496f8..7bb34fe 100644
--- a/gd/l2cap/dynamic_channel.h
+++ b/gd/l2cap/dynamic_channel.h
@@ -18,6 +18,7 @@
 #include "common/bidi_queue.h"
 #include "common/callback.h"
 #include "hci/acl_manager.h"
+#include "l2cap/cid.h"
 #include "os/handler.h"
 #include "packet/base_packet_builder.h"
 #include "packet/packet_view.h"
@@ -42,18 +43,17 @@
     ASSERT(l2cap_handler_ != nullptr);
   }
 
-  hci::Address GetDevice() const;
+  hci::AddressWithType GetDevice() const;
 
   /**
    * Register close callback. If close callback is registered, when a channel is closed, the channel's resource will
    * only be freed after on_close callback is invoked. Otherwise, if no on_close callback is registered, the channel's
    * resource will be freed immediately after closing.
    *
-   * @param user_handler The handler used to invoke the callback on
    * @param on_close_callback The callback invoked upon channel closing.
    */
-  using OnCloseCallback = common::OnceCallback<void(hci::ErrorCode)>;
-  void RegisterOnCloseCallback(os::Handler* user_handler, OnCloseCallback on_close_callback);
+  using OnCloseCallback = common::ContextualOnceCallback<void(hci::ErrorCode)>;
+  void RegisterOnCloseCallback(OnCloseCallback on_close_callback);
 
   /**
    * Indicate that this Dynamic Channel should be closed. OnCloseCallback will be invoked when channel close is done.
@@ -70,6 +70,8 @@
    */
   common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>* GetQueueUpEnd() const;
 
+  Cid HACK_GetRemoteCid();
+
  private:
   std::shared_ptr<l2cap::internal::DynamicChannelImpl> impl_;
   os::Handler* l2cap_handler_;
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller.cc b/gd/l2cap/internal/basic_mode_channel_data_controller.cc
index 3575e25..90ebfab 100644
--- a/gd/l2cap/internal/basic_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller.cc
@@ -27,6 +27,10 @@
     : cid_(cid), remote_cid_(remote_cid), enqueue_buffer_(channel_queue_end), handler_(handler), scheduler_(scheduler) {
 }
 
+BasicModeDataController::~BasicModeDataController() {
+  enqueue_buffer_.Clear();
+}
+
 void BasicModeDataController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
   auto l2cap_information = BasicFrameBuilder::Create(remote_cid_, std::move(sdu));
   pdu_queue_.emplace(std::move(l2cap_information));
@@ -36,6 +40,7 @@
 void BasicModeDataController::OnPdu(packet::PacketView<true> pdu) {
   auto basic_frame_view = BasicFrameView::Create(pdu);
   if (!basic_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
     return;
   }
   enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(basic_frame_view.GetPayload()), handler_);
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller.h b/gd/l2cap/internal/basic_mode_channel_data_controller.h
index 8e40402..df27103 100644
--- a/gd/l2cap/internal/basic_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller.h
@@ -44,6 +44,8 @@
   BasicModeDataController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end, os::Handler* handler,
                           Scheduler* scheduler);
 
+  ~BasicModeDataController() override;
+
   void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
 
   void OnPdu(packet::PacketView<true> pdu) override;
diff --git a/gd/l2cap/internal/data_pipeline_manager.cc b/gd/l2cap/internal/data_pipeline_manager.cc
index 8fd40fb..b19412a 100644
--- a/gd/l2cap/internal/data_pipeline_manager.cc
+++ b/gd/l2cap/internal/data_pipeline_manager.cc
@@ -39,7 +39,9 @@
 }
 
 DataController* DataPipelineManager::GetDataController(Cid cid) {
-  ASSERT(sender_map_.find(cid) != sender_map_.end());
+  if (sender_map_.find(cid) == sender_map_.end()) {
+    return nullptr;
+  };
   return sender_map_.find(cid)->second.GetDataController();
 }
 
diff --git a/gd/l2cap/internal/data_pipeline_manager.h b/gd/l2cap/internal/data_pipeline_manager.h
index 0424de9..aea8d7c 100644
--- a/gd/l2cap/internal/data_pipeline_manager.h
+++ b/gd/l2cap/internal/data_pipeline_manager.h
@@ -71,9 +71,9 @@
  private:
   os::Handler* handler_;
   ILink* link_;
+  std::unordered_map<Cid, Sender> sender_map_;
   std::unique_ptr<Scheduler> scheduler_;
   Receiver receiver_;
-  std::unordered_map<Cid, Sender> sender_map_;
 };
 }  // namespace internal
 }  // namespace l2cap
diff --git a/gd/l2cap/internal/dynamic_channel_allocator.cc b/gd/l2cap/internal/dynamic_channel_allocator.cc
index d0713a4..761a55c0 100644
--- a/gd/l2cap/internal/dynamic_channel_allocator.cc
+++ b/gd/l2cap/internal/dynamic_channel_allocator.cc
@@ -18,18 +18,16 @@
 
 #include "l2cap/cid.h"
 #include "l2cap/classic/internal/link.h"
+#include "l2cap/classic/security_policy.h"
 #include "l2cap/internal/dynamic_channel_allocator.h"
 #include "l2cap/internal/dynamic_channel_impl.h"
-#include "l2cap/security_policy.h"
 #include "os/log.h"
 
 namespace bluetooth {
 namespace l2cap {
 namespace internal {
 
-std::shared_ptr<DynamicChannelImpl> DynamicChannelAllocator::AllocateChannel(Psm psm, Cid remote_cid,
-                                                                             SecurityPolicy security_policy) {
-  ASSERT_LOG(!IsPsmUsed((psm)), "Psm 0x%x for device %s is already in use", psm, link_->GetDevice().ToString().c_str());
+std::shared_ptr<DynamicChannelImpl> DynamicChannelAllocator::AllocateChannel(Psm psm, Cid remote_cid) {
   ASSERT_LOG(IsPsmValid(psm), "Psm 0x%x is invalid", psm);
 
   if (used_remote_cid_.find(remote_cid) != used_remote_cid_.end()) {
@@ -55,9 +53,7 @@
 }
 
 std::shared_ptr<DynamicChannelImpl> DynamicChannelAllocator::AllocateReservedChannel(Cid reserved_cid, Psm psm,
-                                                                                     Cid remote_cid,
-                                                                                     SecurityPolicy security_policy) {
-  ASSERT_LOG(!IsPsmUsed((psm)), "Psm 0x%x for device %s is already in use", psm, link_->GetDevice().ToString().c_str());
+                                                                                     Cid remote_cid) {
   ASSERT_LOG(IsPsmValid(psm), "Psm 0x%x is invalid", psm);
 
   if (used_remote_cid_.find(remote_cid) != used_remote_cid_.end()) {
diff --git a/gd/l2cap/internal/dynamic_channel_allocator.h b/gd/l2cap/internal/dynamic_channel_allocator.h
index 09a3588..9988c51 100644
--- a/gd/l2cap/internal/dynamic_channel_allocator.h
+++ b/gd/l2cap/internal/dynamic_channel_allocator.h
@@ -21,9 +21,9 @@
 
 #include "hci/acl_manager.h"
 #include "l2cap/cid.h"
+#include "l2cap/classic/security_policy.h"
 #include "l2cap/internal/ilink.h"
 #include "l2cap/psm.h"
-#include "l2cap/security_policy.h"
 #include "os/handler.h"
 #include "os/log.h"
 
@@ -45,16 +45,15 @@
 
   // Allocates a channel. If psm is used, OR the remote cid already exists, return nullptr.
   // NOTE: The returned DynamicChannelImpl object is still owned by the channel allocator, NOT the client.
-  std::shared_ptr<DynamicChannelImpl> AllocateChannel(Psm psm, Cid remote_cid, SecurityPolicy security_policy);
+  std::shared_ptr<DynamicChannelImpl> AllocateChannel(Psm psm, Cid remote_cid);
 
-  std::shared_ptr<DynamicChannelImpl> AllocateReservedChannel(Cid reserved_cid, Psm psm, Cid remote_cid,
-                                                              SecurityPolicy security_policy);
+  std::shared_ptr<DynamicChannelImpl> AllocateReservedChannel(Cid reserved_cid, Psm psm, Cid remote_cid);
 
   // Gives an unused Cid to be used for opening a channel. If a channel is used, call AllocateReservedChannel. If no
   // longer needed, use FreeChannel.
   Cid ReserveChannel();
 
-  // Frees a channel. If psm doesn't exist, it will crash
+  // Frees a channel (existing or reserved)
   void FreeChannel(Cid cid);
 
   bool IsPsmUsed(Psm psm) const;
diff --git a/gd/l2cap/internal/dynamic_channel_allocator_fuzz_test.cc b/gd/l2cap/internal/dynamic_channel_allocator_fuzz_test.cc
index c3821dc..be46086 100644
--- a/gd/l2cap/internal/dynamic_channel_allocator_fuzz_test.cc
+++ b/gd/l2cap/internal/dynamic_channel_allocator_fuzz_test.cc
@@ -25,7 +25,7 @@
 namespace internal {
 
 using classic::internal::testing::MockLink;
-using hci::testing::MockAclConnection;
+using hci::testing::MockClassicAclConnection;
 using l2cap::internal::testing::MockParameterProvider;
 using l2cap::internal::testing::MockScheduler;
 using ::testing::NiceMock;
@@ -46,8 +46,8 @@
     thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
     handler_ = new os::Handler(thread_);
     mock_parameter_provider_ = new NiceMock<MockParameterProvider>();
-    mock_classic_link_ =
-        new NiceMock<MockLink>(handler_, mock_parameter_provider_, std::make_unique<NiceMock<MockAclConnection>>());
+    mock_classic_link_ = new NiceMock<MockLink>(handler_, mock_parameter_provider_,
+                                                std::make_unique<NiceMock<MockClassicAclConnection>>(), nullptr);
     EXPECT_CALL(*mock_classic_link_, GetDevice()).WillRepeatedly(Return(device));
     channel_allocator_ = std::make_unique<DynamicChannelAllocator>(mock_classic_link_, handler_);
   }
@@ -83,4 +83,4 @@
 void RunL2capClassicDynamicChannelAllocatorFuzzTest(const uint8_t* data, size_t size) {
   bluetooth::l2cap::internal::L2capClassicDynamicChannelAllocatorFuzzTest test;
   test.RunTests(data, size);
-}
\ No newline at end of file
+}
diff --git a/gd/l2cap/internal/dynamic_channel_allocator_test.cc b/gd/l2cap/internal/dynamic_channel_allocator_test.cc
index 4fc0160..566e2c4 100644
--- a/gd/l2cap/internal/dynamic_channel_allocator_test.cc
+++ b/gd/l2cap/internal/dynamic_channel_allocator_test.cc
@@ -66,7 +66,7 @@
 TEST_F(L2capClassicDynamicChannelAllocatorTest, allocate_and_free_channel) {
   Psm psm = 0x03;
   Cid remote_cid = kFirstDynamicChannel;
-  auto channel = channel_allocator_->AllocateChannel(psm, remote_cid, {});
+  auto channel = channel_allocator_->AllocateChannel(psm, remote_cid);
   Cid local_cid = channel->GetCid();
   EXPECT_TRUE(channel_allocator_->IsPsmUsed(psm));
   EXPECT_EQ(channel, channel_allocator_->FindChannelByCid(local_cid));
@@ -78,7 +78,7 @@
   Psm psm = 0x03;
   Cid remote_cid = kFirstDynamicChannel;
   Cid reserved = channel_allocator_->ReserveChannel();
-  auto channel = channel_allocator_->AllocateReservedChannel(reserved, psm, remote_cid, {});
+  auto channel = channel_allocator_->AllocateReservedChannel(reserved, psm, remote_cid);
   Cid local_cid = channel->GetCid();
   EXPECT_EQ(local_cid, reserved);
   EXPECT_TRUE(channel_allocator_->IsPsmUsed(psm));
diff --git a/gd/l2cap/internal/dynamic_channel_impl.cc b/gd/l2cap/internal/dynamic_channel_impl.cc
index 947a32f..07ebb19 100644
--- a/gd/l2cap/internal/dynamic_channel_impl.cc
+++ b/gd/l2cap/internal/dynamic_channel_impl.cc
@@ -18,10 +18,10 @@
 
 #include "l2cap/cid.h"
 #include "l2cap/classic/internal/link.h"
+#include "l2cap/classic/security_policy.h"
 #include "l2cap/internal/dynamic_channel_impl.h"
 #include "l2cap/internal/sender.h"
 #include "l2cap/psm.h"
-#include "l2cap/security_policy.h"
 #include "os/handler.h"
 #include "os/log.h"
 
@@ -40,19 +40,17 @@
   ASSERT(l2cap_handler_ != nullptr);
 }
 
-hci::Address DynamicChannelImpl::GetDevice() const {
-  return device_.GetAddress();
+hci::AddressWithType DynamicChannelImpl::GetDevice() const {
+  return device_;
 }
 
-void DynamicChannelImpl::RegisterOnCloseCallback(os::Handler* user_handler,
-                                                 DynamicChannel::OnCloseCallback on_close_callback) {
-  ASSERT_LOG(user_handler_ == nullptr, "OnCloseCallback can only be registered once");
+void DynamicChannelImpl::RegisterOnCloseCallback(DynamicChannel::OnCloseCallback on_close_callback) {
+  ASSERT_LOG(on_close_callback_.IsEmpty(), "OnCloseCallback can only be registered once");
   // If channel is already closed, call the callback immediately without saving it
   if (closed_) {
-    user_handler->Post(common::BindOnce(std::move(on_close_callback), close_reason_));
+    on_close_callback.Invoke(close_reason_);
     return;
   }
-  user_handler_ = user_handler;
   on_close_callback_ = std::move(on_close_callback);
 }
 
@@ -67,13 +65,8 @@
   close_reason_ = status;
   link_ = nullptr;
   l2cap_handler_ = nullptr;
-  if (user_handler_ == nullptr) {
-    return;
-  }
-  // On close callback can only be called once
-  user_handler_->Post(common::BindOnce(std::move(on_close_callback_), status));
-  user_handler_ = nullptr;
-  on_close_callback_.Reset();
+  on_close_callback_.InvokeIfNotEmpty(close_reason_);
+  on_close_callback_ = {};
 }
 
 std::string DynamicChannelImpl::ToString() {
diff --git a/gd/l2cap/internal/dynamic_channel_impl.h b/gd/l2cap/internal/dynamic_channel_impl.h
index 856e21d..c54332e 100644
--- a/gd/l2cap/internal/dynamic_channel_impl.h
+++ b/gd/l2cap/internal/dynamic_channel_impl.h
@@ -38,9 +38,9 @@
 
   virtual ~DynamicChannelImpl() = default;
 
-  hci::Address GetDevice() const;
+  hci::AddressWithType GetDevice() const;
 
-  virtual void RegisterOnCloseCallback(os::Handler* user_handler, DynamicChannel::OnCloseCallback on_close_callback);
+  virtual void RegisterOnCloseCallback(DynamicChannel::OnCloseCallback on_close_callback);
 
   virtual void Close();
   virtual void OnClosed(hci::ErrorCode status);
@@ -78,13 +78,12 @@
   const hci::AddressWithType device_;
 
   // User supported states
-  os::Handler* user_handler_ = nullptr;
   DynamicChannel::OnCloseCallback on_close_callback_{};
 
   // Internal states
   bool closed_ = false;
   hci::ErrorCode close_reason_ = hci::ErrorCode::SUCCESS;
-  static constexpr size_t kChannelQueueSize = 10;
+  static constexpr size_t kChannelQueueSize = 5;
   common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder> channel_queue_{
       kChannelQueueSize};
 
diff --git a/gd/l2cap/internal/dynamic_channel_impl_test.cc b/gd/l2cap/internal/dynamic_channel_impl_test.cc
index 677b504..7469869 100644
--- a/gd/l2cap/internal/dynamic_channel_impl_test.cc
+++ b/gd/l2cap/internal/dynamic_channel_impl_test.cc
@@ -58,6 +58,14 @@
   os::Handler* l2cap_handler_ = nullptr;
 };
 
+class StatusCapture {
+ public:
+  hci::ErrorCode value = hci::ErrorCode::SUCCESS;
+  void capture(hci::ErrorCode status) {
+    value = status;
+  }
+};
+
 TEST_F(L2capClassicDynamicChannelImplTest, get_device) {
   MockParameterProvider mock_parameter_provider;
   MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
@@ -65,7 +73,7 @@
   EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
   DynamicChannelImpl dynamic_channel_impl(0x01, kFirstDynamicChannel, kFirstDynamicChannel, &mock_classic_link,
                                           l2cap_handler_);
-  EXPECT_EQ(device.GetAddress(), dynamic_channel_impl.GetDevice());
+  EXPECT_EQ(device, dynamic_channel_impl.GetDevice());
 }
 
 TEST_F(L2capClassicDynamicChannelImplTest, close_triggers_callback) {
@@ -78,16 +86,16 @@
 
   // Register on close callback
   auto user_handler = std::make_unique<os::Handler>(thread_);
-  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
-  dynamic_channel_impl.RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+  StatusCapture* my_status = new StatusCapture();
+  dynamic_channel_impl.RegisterOnCloseCallback(user_handler->BindOnceOn(my_status, &StatusCapture::capture));
 
   // Channel closure should trigger such callback
   dynamic_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
   SyncHandler(user_handler.get());
-  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status->value);
 
   user_handler->Clear();
+  delete my_status;
 }
 
 TEST_F(L2capClassicDynamicChannelImplTest, register_callback_after_close_should_call_immediately) {
@@ -103,13 +111,14 @@
 
   // Register on close callback should trigger callback immediately
   auto user_handler = std::make_unique<os::Handler>(thread_);
-  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
-  dynamic_channel_impl.RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+  StatusCapture* my_status = new StatusCapture();
+  dynamic_channel_impl.RegisterOnCloseCallback(user_handler->BindOnceOn(my_status, &StatusCapture::capture));
+
   SyncHandler(user_handler.get());
-  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status->value);
 
   user_handler->Clear();
+  delete my_status;
 }
 
 TEST_F(L2capClassicDynamicChannelImplTest, close_twice_should_fail) {
@@ -122,19 +131,19 @@
 
   // Register on close callback
   auto user_handler = std::make_unique<os::Handler>(thread_);
-  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
-  dynamic_channel_impl.RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+  StatusCapture* my_status = new StatusCapture();
+  dynamic_channel_impl.RegisterOnCloseCallback(user_handler->BindOnceOn(my_status, &StatusCapture::capture));
 
   // Channel closure should trigger such callback
   dynamic_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
   SyncHandler(user_handler.get());
-  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status->value);
 
   // 2nd OnClose() callback should fail
   EXPECT_DEATH(dynamic_channel_impl.OnClosed(hci::ErrorCode::PAGE_TIMEOUT), ".*OnClosed.*");
 
   user_handler->Clear();
+  delete my_status;
 }
 
 TEST_F(L2capClassicDynamicChannelImplTest, multiple_registeration_should_fail) {
@@ -147,15 +156,15 @@
 
   // Register on close callback
   auto user_handler = std::make_unique<os::Handler>(thread_);
-  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
-  dynamic_channel_impl.RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+  StatusCapture* my_status = new StatusCapture();
+  dynamic_channel_impl.RegisterOnCloseCallback(user_handler->BindOnceOn(my_status, &StatusCapture::capture));
 
-  EXPECT_DEATH(dynamic_channel_impl.RegisterOnCloseCallback(user_handler.get(),
-                                                            common::BindOnce([](hci::ErrorCode status) { FAIL(); })),
-               ".*RegisterOnCloseCallback.*");
+  EXPECT_DEATH(
+      dynamic_channel_impl.RegisterOnCloseCallback(user_handler->BindOnce([](hci::ErrorCode status) { FAIL(); })),
+      ".*RegisterOnCloseCallback.*");
 
   user_handler->Clear();
+  delete my_status;
 }
 
 }  // namespace internal
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
index 23287f7..fa142dd 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
@@ -34,7 +34,9 @@
     : link_(link), cid_(cid), remote_cid_(remote_cid), enqueue_buffer_(channel_queue_end), handler_(handler),
       scheduler_(scheduler), pimpl_(std::make_unique<impl>(this, handler)) {}
 
-ErtmController::~ErtmController() = default;
+ErtmController::~ErtmController() {
+  enqueue_buffer_.Clear();
+}
 
 struct ErtmController::impl {
   impl(ErtmController* controller, os::Handler* handler)
@@ -165,6 +167,7 @@
       send_rr_or_rnr(Poll::POLL);
       start_monitor_timer();
     } else if (tx_state_ == TxState::WAIT_F) {
+      LOG_INFO("Close channel because max transmit reached");
       CloseChannel();
     }
   }
@@ -409,6 +412,7 @@
         remote_busy_ = false;
         pass_to_tx(req_seq, f);
         retransmit_requested_i_frame(req_seq, p);
+        send_pending_i_frames();
         if (p_bit_outstanding()) {
           srej_actioned_ = true;
           srej_save_req_seq_ = req_seq;
@@ -486,41 +490,57 @@
     return retry_count_ < controller_->local_max_transmit_;
   }
 
+  // Compares two sequence numbers (tx_seq or rx_seq)
+  bool sequence_less_than(uint8_t x, uint8_t y) {
+    // Assuming the maximum overflow of sequence number is the same as local_tx_window_ (10 by default).
+    return x < y || kMaxTxWin - (x - y) < controller_->local_tx_window_;
+  }
+
+  // Compares two sequence numbers (tx_seq or rx_seq)
+  bool sequence_less_than_or_equal(uint8_t x, uint8_t y) {
+    // Assuming the maximum overflow of sequence number is the same as local_tx_window_ (10 by default).
+    return x <= y || kMaxTxWin - (x - y) <= controller_->local_tx_window_;
+  }
+
   bool with_expected_tx_seq(uint8_t tx_seq) {
     return tx_seq == expected_tx_seq_;
   }
 
   bool with_valid_req_seq(uint8_t req_seq) {
-    return expected_ack_seq_ <= req_seq && req_seq <= next_tx_seq_;
+    return sequence_less_than_or_equal(expected_ack_seq_, req_seq) &&
+           sequence_less_than_or_equal(req_seq, next_tx_seq_);
   }
 
   bool with_valid_req_seq_retrans(uint8_t req_seq) {
-    return expected_ack_seq_ <= req_seq && req_seq <= next_tx_seq_;
+    return sequence_less_than_or_equal(expected_ack_seq_, req_seq) &&
+           sequence_less_than_or_equal(req_seq, next_tx_seq_);
   }
 
   bool with_valid_f_bit(Final f) {
-    return f == Final::NOT_SET || tx_state_ == TxState::WAIT_F;
+    return f == Final::NOT_SET ^ tx_state_ == TxState::WAIT_F;
   }
 
   bool with_unexpected_tx_seq(uint8_t tx_seq) {
-    return tx_seq > expected_tx_seq_ && tx_seq <= expected_tx_seq_ + controller_->local_tx_window_;
+    return sequence_less_than(expected_tx_seq_, tx_seq) &&
+           sequence_less_than_or_equal(tx_seq, expected_tx_seq_ + controller_->local_tx_window_);
   }
 
   bool with_duplicate_tx_seq(uint8_t tx_seq) {
-    return tx_seq < expected_tx_seq_ && tx_seq >= expected_tx_seq_ - controller_->local_tx_window_;
+    return sequence_less_than(tx_seq, expected_tx_seq_) &&
+           sequence_less_than_or_equal(expected_tx_seq_ - controller_->local_tx_window_, tx_seq);
   }
 
   bool with_invalid_tx_seq(uint8_t tx_seq) {
-    return tx_seq < expected_tx_seq_ - controller_->local_tx_window_ ||
-           tx_seq > expected_tx_seq_ + controller_->local_tx_window_;
+    return sequence_less_than(tx_seq, expected_tx_seq_ - controller_->local_tx_window_) ||
+           sequence_less_than(expected_tx_seq_ + controller_->local_tx_window_, tx_seq);
   }
 
   bool with_invalid_req_seq(uint8_t req_seq) {
-    return req_seq < expected_ack_seq_ || req_seq >= next_tx_seq_;
+    return sequence_less_than(req_seq, expected_ack_seq_) || sequence_less_than(next_tx_seq_, req_seq);
   }
 
   bool with_invalid_req_seq_retrans(uint8_t req_seq) {
-    return req_seq < expected_ack_seq_ || req_seq >= next_tx_seq_;
+    return sequence_less_than(req_seq, expected_ack_seq_) || sequence_less_than(next_tx_seq_, req_seq);
   }
 
   bool not_with_expected_tx_seq(uint8_t tx_seq) {
@@ -603,6 +623,7 @@
       retry_i_frames_[i] = 0;
     }
     unacked_frames_ -= ((req_seq - expected_ack_seq_) + kMaxTxWin) % kMaxTxWin;
+    expected_ack_seq_ = req_seq;
     if (unacked_frames_ == 0) {
       stop_retrans_timer();
     }
@@ -632,6 +653,7 @@
 
   void send_rnr(Final f) {
     _send_s_frame(SupervisoryFunction::RECEIVER_NOT_READY, expected_tx_seq_, Poll::NOT_SET, f);
+    rnr_sent_ = true;
   }
 
   void send_rej(Poll p = Poll::NOT_SET, Final f = Final::NOT_SET) {
@@ -719,7 +741,7 @@
   }
 
   void store_or_ignore() {
-    // We choose to ignore. We don't support local busy so far.
+    // We choose to ignore.
   }
 
   bool p_bit_outstanding() {
@@ -729,19 +751,23 @@
   void retransmit_i_frames(uint8_t req_seq, Poll p = Poll::NOT_SET) {
     uint8_t i = req_seq;
     Final f = (p == Poll::NOT_SET ? Final::NOT_SET : Final::POLL_RESPONSE);
-    while (unacked_list_.find(i) == unacked_list_.end()) {
+    while (unacked_list_.find(i) != unacked_list_.end()) {
+      if (retry_i_frames_[i] == controller_->local_max_transmit_) {
+        CloseChannel();
+        return;
+      }
       std::unique_ptr<CopyablePacketBuilder> copyable_packet_builder =
           std::make_unique<CopyablePacketBuilder>(std::get<2>(unacked_list_.find(i)->second));
       _send_i_frame(std::get<0>(unacked_list_.find(i)->second), std::move(copyable_packet_builder), buffer_seq_, i,
                     std::get<1>(unacked_list_.find(i)->second), f);
       retry_i_frames_[i]++;
-      if (retry_i_frames_[i] == controller_->local_max_transmit_) {
-        CloseChannel();
-      }
       frames_sent_++;
       f = Final::NOT_SET;
+      i++;
     }
-    start_retrans_timer();
+    if (i != req_seq) {
+      start_retrans_timer();
+    }
   }
 
   void retransmit_requested_i_frame(uint8_t req_seq, Poll p) {
@@ -787,7 +813,9 @@
 void ErtmController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
   auto sdu_size = sdu->size();
   std::vector<std::unique_ptr<packet::RawBuilder>> segments;
-  packet::FragmentingInserter fragmenting_inserter(size_each_packet_, std::back_insert_iterator(segments));
+  auto size_each_packet = (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Enhanced control */ -
+                           (fcs_enabled_ ? 2 : 0));
+  packet::FragmentingInserter fragmenting_inserter(size_each_packet, std::back_insert_iterator(segments));
   sdu->Serialize(fragmenting_inserter);
   fragmenting_inserter.finalize();
   if (segments.size() == 1) {
@@ -937,6 +965,8 @@
 
 void ErtmController::stage_for_reassembly(SegmentationAndReassembly sar, uint16_t sdu_size,
                                           const packet::PacketView<kLittleEndian>& payload) {
+  // If EnqueueBuffer has more than 1 packets, we claim LocalBusy, until queue is empty
+  constexpr size_t kEnqueueBufferBusyThreshold = 3;
   switch (sar) {
     case SegmentationAndReassembly::UNSEGMENTED:
       if (sar_state_ != SegmentationAndReassembly::END) {
@@ -946,6 +976,10 @@
       }
       // TODO: Enforce MTU
       enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(payload), handler_);
+      if (enqueue_buffer_.Size() == kEnqueueBufferBusyThreshold) {
+        pimpl_->local_busy_detected();
+        enqueue_buffer_.NotifyOnEmpty(common::BindOnce(&impl::local_busy_clear, common::Unretained(pimpl_.get())));
+      }
       break;
     case SegmentationAndReassembly::START:
       if (sar_state_ != SegmentationAndReassembly::END) {
@@ -984,6 +1018,10 @@
       }
       reassembly_stage_.AppendPacketView(payload);
       enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(reassembly_stage_), handler_);
+      if (enqueue_buffer_.Size() == kEnqueueBufferBusyThreshold) {
+        pimpl_->local_busy_detected();
+        enqueue_buffer_.NotifyOnEmpty(common::BindOnce(&impl::local_busy_clear, common::Unretained(pimpl_.get())));
+      }
       break;
   }
 }
@@ -999,10 +1037,11 @@
 
 void ErtmController::SetRetransmissionAndFlowControlOptions(
     const RetransmissionAndFlowControlConfigurationOption& option) {
-  local_tx_window_ = option.tx_window_size_;
+  remote_tx_window_ = option.tx_window_size_;
   local_max_transmit_ = option.max_transmit_;
   local_retransmit_timeout_ms_ = option.retransmission_time_out_;
   local_monitor_timeout_ms_ = option.monitor_time_out_;
+  remote_mps_ = option.maximum_pdu_size_;
 }
 
 void ErtmController::close_channel() {
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
index 184d6c1..8f000a9 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
@@ -72,9 +72,6 @@
   uint16_t remote_tx_window_ = 10;
   uint16_t remote_mps_ = 1010;
 
-  uint16_t size_each_packet_ =
-      (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
-
   class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
    public:
     PacketViewForReassembly(const PacketView& packetView) : PacketView(packetView) {}
diff --git a/gd/l2cap/internal/fixed_channel_allocator.h b/gd/l2cap/internal/fixed_channel_allocator.h
index b821cb1..7d67033 100644
--- a/gd/l2cap/internal/fixed_channel_allocator.h
+++ b/gd/l2cap/internal/fixed_channel_allocator.h
@@ -21,7 +21,6 @@
 
 #include "hci/hci_packets.h"
 #include "l2cap/cid.h"
-#include "l2cap/security_policy.h"
 #include "os/handler.h"
 #include "os/log.h"
 
@@ -43,7 +42,7 @@
 
   // Allocates a channel. If cid is used, return nullptr. NOTE: The returned BaseFixedChannelImpl object is still
   // owned by the channel allocator, NOT the client.
-  virtual std::shared_ptr<FixedChannelImplType> AllocateChannel(Cid cid, SecurityPolicy security_policy) {
+  virtual std::shared_ptr<FixedChannelImplType> AllocateChannel(Cid cid) {
     ASSERT_LOG(!IsChannelAllocated((cid)), "Cid 0x%x for link %s is already in use", cid, link_->ToString().c_str());
     ASSERT_LOG(cid >= kFirstFixedChannel && cid <= kLastFixedChannel, "Cid %d out of bound", cid);
     auto elem = channels_.try_emplace(cid, std::make_shared<FixedChannelImplType>(cid, link_, l2cap_handler_));
diff --git a/gd/l2cap/internal/fixed_channel_allocator_test.cc b/gd/l2cap/internal/fixed_channel_allocator_test.cc
index 38eac13..5295cc7 100644
--- a/gd/l2cap/internal/fixed_channel_allocator_test.cc
+++ b/gd/l2cap/internal/fixed_channel_allocator_test.cc
@@ -69,7 +69,7 @@
 
 TEST_F(L2capFixedChannelAllocatorTest, allocate_and_free_channel) {
   Cid cid = kFirstFixedChannel;
-  auto channel = channel_allocator_->AllocateChannel(cid, {});
+  auto channel = channel_allocator_->AllocateChannel(cid);
   EXPECT_TRUE(channel_allocator_->IsChannelAllocated(cid));
   EXPECT_EQ(channel, channel_allocator_->FindChannel(cid));
   ASSERT_NO_FATAL_FAILURE(channel_allocator_->FreeChannel(cid));
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
index 943cb17..6769763 100644
--- a/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
@@ -102,6 +102,7 @@
 }
 
 std::unique_ptr<packet::BasePacketBuilder> LeCreditBasedDataController::GetNextPacket() {
+  ASSERT(!pdu_queue_.empty());
   auto next = std::move(pdu_queue_.front());
   pdu_queue_.pop();
   return next;
@@ -123,6 +124,7 @@
   credits_ = total_credits;
   if (pending_frames_count_ > 0 && credits_ >= pending_frames_count_) {
     scheduler_->OnPacketsReady(cid_, pending_frames_count_);
+    pending_frames_count_ = 0;
     credits_ -= pending_frames_count_;
   } else if (pending_frames_count_ > 0) {
     scheduler_->OnPacketsReady(cid_, credits_);
diff --git a/gd/l2cap/internal/receiver.cc b/gd/l2cap/internal/receiver.cc
index 86f43ae..8af6455 100644
--- a/gd/l2cap/internal/receiver.cc
+++ b/gd/l2cap/internal/receiver.cc
@@ -33,10 +33,12 @@
                                       common::Bind(&Receiver::link_queue_dequeue_callback, common::Unretained(this)));
 }
 
+// Invoked from external handler/thread (ModuleRegistry)
 Receiver::~Receiver() {
   link_queue_up_end_->UnregisterDequeue();
 }
 
+// Invoked from external (Queue Reactable)
 void Receiver::link_queue_dequeue_callback() {
   auto packet = link_queue_up_end_->TryDequeue();
   auto basic_frame_view = BasicFrameView::Create(*packet);
@@ -47,6 +49,7 @@
   Cid cid = static_cast<Cid>(basic_frame_view.GetChannelId());
   auto* data_controller = data_pipeline_manager_->GetDataController(cid);
   if (data_controller == nullptr) {
+    // TODO(b/150170271): Buffer a few packets before data controller is attached
     LOG_WARN("Received a packet with invalid cid: %d", cid);
     return;
   }
diff --git a/gd/l2cap/internal/scheduler_fifo.cc b/gd/l2cap/internal/scheduler_fifo.cc
index 8d2a64a..c70c401 100644
--- a/gd/l2cap/internal/scheduler_fifo.cc
+++ b/gd/l2cap/internal/scheduler_fifo.cc
@@ -30,12 +30,15 @@
   ASSERT(link_queue_up_end_ != nullptr && handler_ != nullptr);
 }
 
+// Invoked from some external Handler context
 Fifo::~Fifo() {
-  if (link_queue_enqueue_registered_) {
+  // TODO(hsz): notify Sender don't send callback to me
+  if (link_queue_enqueue_registered_.exchange(false)) {
     link_queue_up_end_->UnregisterEnqueue();
   }
 }
 
+// Invoked within L2CAP Handler context
 void Fifo::OnPacketsReady(Cid cid, int number_packets) {
   if (number_packets == 0) {
     return;
@@ -44,6 +47,7 @@
   try_register_link_queue_enqueue();
 }
 
+// Invoked from some external Queue Reactable context
 std::unique_ptr<Fifo::UpperDequeue> Fifo::link_queue_enqueue_callback() {
   ASSERT(!next_to_dequeue_and_num_packets.empty());
   auto& channel_id_and_number_packets = next_to_dequeue_and_num_packets.front();
@@ -55,20 +59,18 @@
   auto packet = data_pipeline_manager_->GetDataController(channel_id)->GetNextPacket();
 
   data_pipeline_manager_->OnPacketSent(channel_id);
-  if (next_to_dequeue_and_num_packets.empty()) {
+  if (next_to_dequeue_and_num_packets.empty() && link_queue_enqueue_registered_.exchange(false)) {
     link_queue_up_end_->UnregisterEnqueue();
-    link_queue_enqueue_registered_ = false;
   }
   return packet;
 }
 
 void Fifo::try_register_link_queue_enqueue() {
-  if (link_queue_enqueue_registered_) {
+  if (link_queue_enqueue_registered_.exchange(true)) {
     return;
   }
   link_queue_up_end_->RegisterEnqueue(handler_,
                                       common::Bind(&Fifo::link_queue_enqueue_callback, common::Unretained(this)));
-  link_queue_enqueue_registered_ = true;
 }
 
 }  // namespace internal
diff --git a/gd/l2cap/internal/scheduler_fifo.h b/gd/l2cap/internal/scheduler_fifo.h
index f6fcf7f..e893a4e 100644
--- a/gd/l2cap/internal/scheduler_fifo.h
+++ b/gd/l2cap/internal/scheduler_fifo.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <atomic>
 #include <string>
 #include <unordered_map>
 
@@ -44,7 +45,7 @@
   LowerQueueUpEnd* link_queue_up_end_;
   os::Handler* handler_;
   std::queue<std::pair<Cid, int>> next_to_dequeue_and_num_packets;
-  bool link_queue_enqueue_registered_ = false;
+  std::atomic_bool link_queue_enqueue_registered_ = false;
 
   void try_register_link_queue_enqueue();
   std::unique_ptr<LowerEnqueue> link_queue_enqueue_callback();
diff --git a/gd/l2cap/internal/sender.cc b/gd/l2cap/internal/sender.cc
index a68d264..2c740ca 100644
--- a/gd/l2cap/internal/sender.cc
+++ b/gd/l2cap/internal/sender.cc
@@ -55,7 +55,7 @@
 }
 
 Sender::~Sender() {
-  if (is_dequeue_registered_) {
+  if (is_dequeue_registered_.exchange(false)) {
     queue_end_->UnregisterDequeue();
   }
 }
@@ -73,19 +73,21 @@
 }
 
 void Sender::try_register_dequeue() {
-  if (is_dequeue_registered_) {
+  if (is_dequeue_registered_.exchange(true)) {
     return;
   }
   queue_end_->RegisterDequeue(handler_, common::Bind(&Sender::dequeue_callback, common::Unretained(this)));
-  is_dequeue_registered_ = true;
 }
 
+// From external context
 void Sender::dequeue_callback() {
   auto packet = queue_end_->TryDequeue();
   ASSERT(packet != nullptr);
-  data_controller_->OnSdu(std::move(packet));
-  queue_end_->UnregisterDequeue();
-  is_dequeue_registered_ = false;
+  handler_->Post(
+      common::BindOnce(&DataController::OnSdu, common::Unretained(data_controller_.get()), std::move(packet)));
+  if (is_dequeue_registered_.exchange(false)) {
+    queue_end_->UnregisterDequeue();
+  }
 }
 
 void Sender::UpdateClassicConfiguration(classic::internal::ChannelConfigurationState config) {
@@ -101,7 +103,9 @@
   if (mode == RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION) {
     data_controller_ =
         std::make_unique<ErtmController>(link_, channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
-    data_controller_->SetRetransmissionAndFlowControlOptions(config.local_retransmission_and_flow_control_);
+    RetransmissionAndFlowControlConfigurationOption option = config.local_retransmission_and_flow_control_;
+    option.tx_window_size_ = config.remote_retransmission_and_flow_control_.tx_window_size_;
+    data_controller_->SetRetransmissionAndFlowControlOptions(option);
     data_controller_->EnableFcs(config.fcs_type_ == FcsType::DEFAULT);
     return;
   }
diff --git a/gd/l2cap/internal/sender.h b/gd/l2cap/internal/sender.h
index 540064f..3e541a96 100644
--- a/gd/l2cap/internal/sender.h
+++ b/gd/l2cap/internal/sender.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <atomic>
 #include <string>
 #include <unordered_map>
 
@@ -81,7 +82,7 @@
   Scheduler* scheduler_;
   const Cid channel_id_;
   const Cid remote_channel_id_;
-  bool is_dequeue_registered_ = false;
+  std::atomic_bool is_dequeue_registered_ = false;
   RetransmissionAndFlowControlModeOption mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
   std::unique_ptr<DataController> data_controller_;
 
diff --git a/gd/l2cap/l2cap_packet_fuzz_test.cc b/gd/l2cap/l2cap_packet_fuzz_test.cc
index 6fecafc..90bd965 100644
--- a/gd/l2cap/l2cap_packet_fuzz_test.cc
+++ b/gd/l2cap/l2cap_packet_fuzz_test.cc
@@ -46,7 +46,7 @@
 }  // namespace bluetooth
 
 void RunL2capPacketFuzzTest(const uint8_t* data, size_t size) {
-  if (data == nullptr) return;
+  if (data == nullptr || size > 65536 /* Max ACL packet size */) return;
   for (auto test_function : bluetooth::l2cap::l2cap_packet_fuzz_tests) {
     test_function(data, size);
   }
diff --git a/gd/l2cap/l2cap_packet_test.cc b/gd/l2cap/l2cap_packet_test.cc
index 36b8791..7a413f4 100644
--- a/gd/l2cap/l2cap_packet_test.cc
+++ b/gd/l2cap/l2cap_packet_test.cc
@@ -61,5 +61,95 @@
 
 std::vector<uint8_t> config_mtu_request = {0x04, 0x05, 0x08, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x02, 0xa0, 0x02};
 DEFINE_AND_INSTANTIATE_ConfigurationRequestReflectionTest(config_mtu_request);
+
+std::vector<uint8_t> config_request_one_defined_option = {0x04, 0x05, 0x08, 0x00, 0x41, 0x00,
+                                                          0x00, 0x00, 0x01, 0x02, 0x12, 0x34};
+std::vector<uint8_t> config_request_two_defined_options = {0x04, 0x05, 0x0c, 0x00, 0x41, 0x00, 0x00, 0x00,
+                                                           0x01, 0x02, 0x12, 0x34, 0x02, 0x02, 0x56, 0x78};
+std::vector<uint8_t> config_request_two_undefined_options = {0x04, 0x05, 0x0e, 0x00, 0x41, 0x00, 0x00, 0x00, 0x7f,
+                                                             0x02, 0x01, 0x00, 0x7e, 0x04, 0x11, 0x11, 0x00, 0x00};
+std::vector<uint8_t> config_request_hint_one_defined_option = {0x04, 0x05, 0x08, 0x00, 0x41, 0x00,
+                                                               0x00, 0x00, 0x81, 0x02, 0x12, 0x34};
+std::vector<uint8_t> config_request_hint_two_undefined_options = {0x04, 0x05, 0x0c, 0x00, 0x41, 0x00, 0x00, 0x00,
+                                                                  0x90, 0x02, 0x01, 0x00, 0x91, 0x02, 0x11, 0x11};
+TEST(L2capPacketsTest, testConfigRequestOptions) {
+  {
+    std::shared_ptr<std::vector<uint8_t>> view_bytes =
+        std::make_shared<std::vector<uint8_t>>(config_request_one_defined_option);
+
+    PacketView<kLittleEndian> packet_bytes_view(view_bytes);
+    auto view = ConfigurationRequestView::Create(ControlView::Create(packet_bytes_view));
+    ASSERT_TRUE(view.IsValid());
+    ASSERT_EQ(1, view.GetConfig().size());
+  }
+
+  {
+    std::shared_ptr<std::vector<uint8_t>> view_bytes =
+        std::make_shared<std::vector<uint8_t>>(config_request_two_defined_options);
+
+    PacketView<kLittleEndian> packet_bytes_view(view_bytes);
+    auto view = ConfigurationRequestView::Create(ControlView::Create(packet_bytes_view));
+    ASSERT_TRUE(view.IsValid());
+    ASSERT_EQ(2, view.GetConfig().size());
+  }
+
+  {
+    std::shared_ptr<std::vector<uint8_t>> view_bytes =
+        std::make_shared<std::vector<uint8_t>>(config_request_two_undefined_options);
+
+    PacketView<kLittleEndian> packet_bytes_view(view_bytes);
+    auto view = ConfigurationRequestView::Create(ControlView::Create(packet_bytes_view));
+    ASSERT_TRUE(view.IsValid());
+    ASSERT_EQ(2, view.GetConfig().size());
+  }
+
+  {
+    std::shared_ptr<std::vector<uint8_t>> view_bytes =
+        std::make_shared<std::vector<uint8_t>>(config_request_hint_one_defined_option);
+
+    PacketView<kLittleEndian> packet_bytes_view(view_bytes);
+    auto view = ConfigurationRequestView::Create(ControlView::Create(packet_bytes_view));
+    ASSERT_TRUE(view.IsValid());
+    ASSERT_EQ(1, view.GetConfig().size());
+  }
+
+  {
+    std::shared_ptr<std::vector<uint8_t>> view_bytes =
+        std::make_shared<std::vector<uint8_t>>(config_request_hint_two_undefined_options);
+
+    PacketView<kLittleEndian> packet_bytes_view(view_bytes);
+    auto view = ConfigurationRequestView::Create(ControlView::Create(packet_bytes_view));
+    ASSERT_TRUE(view.IsValid());
+    ASSERT_EQ(2, view.GetConfig().size());
+  }
+}
+
+DEFINE_ConfigurationRequestReflectionFuzzTest();
+
+TEST(L2capFuzzRegressions, ConfigurationRequestFuzz_5691566077247488) {
+  uint8_t bluetooth_gd_fuzz_test_5691566077247488[] = {
+      0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  };
+  RunConfigurationRequestReflectionFuzzTest(bluetooth_gd_fuzz_test_5691566077247488,
+                                            sizeof(bluetooth_gd_fuzz_test_5691566077247488));
+}
+
+TEST(L2capFuzzRegressions, ConfigurationRequestFuzz_5747922062802944) {
+  uint8_t bluetooth_gd_fuzz_test_5747922062802944[] = {
+      0x04, 0x02, 0x02, 0x7f, 0x3f, 0x7f, 0x3f, 0x7e, 0x7f,
+  };
+  RunConfigurationRequestReflectionFuzzTest(bluetooth_gd_fuzz_test_5747922062802944,
+                                            sizeof(bluetooth_gd_fuzz_test_5747922062802944));
+}
+
+TEST(L2capFuzzRegressions, ConfigurationRequestFuzz_5202709231697920) {
+  uint8_t bluetooth_gd_fuzz_test_5747922062802944[] = {
+      0x04, 0x01, 0x45, 0x45, 0x05, 0x01, 0x01, 0x45, 0x05, 0x01,
+  };
+
+  RunConfigurationRequestReflectionFuzzTest(bluetooth_gd_fuzz_test_5747922062802944,
+                                            sizeof(bluetooth_gd_fuzz_test_5747922062802944));
+}
+
 }  // namespace l2cap
 }  // namespace bluetooth
diff --git a/gd/l2cap/l2cap_packets.pdl b/gd/l2cap/l2cap_packets.pdl
index ae44cb1..943f068 100644
--- a/gd/l2cap/l2cap_packets.pdl
+++ b/gd/l2cap/l2cap_packets.pdl
@@ -309,15 +309,15 @@
 struct ConfigurationOption {
   type : ConfigurationOptionType,
   is_hint : ConfigurationOptionIsHint,
-  length : 8,
+  _size_(_body_) : 8,
   _body_,
 }
 
-struct MtuConfigurationOption : ConfigurationOption (type = MTU, length = 2) {
+struct MtuConfigurationOption : ConfigurationOption (type = MTU) {
   mtu : 16,
 }
 
-struct FlushTimeoutConfigurationOption : ConfigurationOption (type = FLUSH_TIMEOUT, length = 2) {
+struct FlushTimeoutConfigurationOption : ConfigurationOption (type = FLUSH_TIMEOUT) {
   flush_timeout : 16,
 }
 
@@ -327,7 +327,7 @@
   GUARANTEED = 0x02,
 }
 
-struct QualityOfServiceConfigurationOption : ConfigurationOption (type = QUALITY_OF_SERVICE, length = 22) {
+struct QualityOfServiceConfigurationOption : ConfigurationOption (type = QUALITY_OF_SERVICE) {
   _reserved_ : 8, // Flags
   service_type : QosServiceType,
   token_rate : 32,         // 0 = ignore, 0xffffffff = max available
@@ -346,7 +346,7 @@
 }
 
 
-struct RetransmissionAndFlowControlConfigurationOption : ConfigurationOption (type = RETRANSMISSION_AND_FLOW_CONTROL, length = 9) {
+struct RetransmissionAndFlowControlConfigurationOption : ConfigurationOption (type = RETRANSMISSION_AND_FLOW_CONTROL) {
   mode : RetransmissionAndFlowControlModeOption,
   tx_window_size : 8, // 1-32 for Flow Control and Retransmission, 1-63 for Enhanced
   max_transmit : 8,
@@ -360,12 +360,12 @@
   DEFAULT = 1,  // 16-bit FCS
 }
 
-struct FrameCheckSequenceOption : ConfigurationOption (type = FRAME_CHECK_SEQUENCE, length = 1) {
+struct FrameCheckSequenceOption : ConfigurationOption (type = FRAME_CHECK_SEQUENCE) {
   fcs_type : FcsType,
 }
 
 
-struct ExtendedFlowSpecificationOption : ConfigurationOption (type = EXTENDED_FLOW_SPECIFICATION, length = 16) {
+struct ExtendedFlowSpecificationOption : ConfigurationOption (type = EXTENDED_FLOW_SPECIFICATION) {
   identifier : 8, // Default 0x01, must be 0x01 for Extended Flow-Best-Effort
   service_type : QosServiceType,
   maximum_sdu_size : 16, // Octets
@@ -374,7 +374,7 @@
   flush_timeout : 32, // in microseconds 0x0 = no retransmissions 0xFFFFFFFF = never flushed
 }
 
-struct ExtendedWindowSizeOption : ConfigurationOption (type = EXTENDED_WINDOW_SIZE, length = 2) {
+struct ExtendedWindowSizeOption : ConfigurationOption (type = EXTENDED_WINDOW_SIZE) {
   max_window_size : 16, // 0x0000 = Valid for streaming, 0x0001-0x3FFF Valid for Enhanced Retransmission
 }
 
diff --git a/gd/l2cap/le/cert/cert_le_l2cap.py b/gd/l2cap/le/cert/cert_le_l2cap.py
new file mode 100644
index 0000000..fe691a6
--- /dev/null
+++ b/gd/l2cap/le/cert/cert_le_l2cap.py
@@ -0,0 +1,201 @@
+#!/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 Closable
+from cert.closable import safeClose
+from cert.py_le_acl_manager import PyLeAclManager
+from cert.truth import assertThat
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import l2cap_packets
+from bluetooth_packets_python3.l2cap_packets import LeCommandCode
+from bluetooth_packets_python3.l2cap_packets import LeCreditBasedConnectionResponseResult
+from cert.event_stream import FilteringEventStream
+from cert.event_stream import IEventStream
+from cert.matchers import L2capMatchers
+from cert.captures import L2capCaptures
+from mobly import asserts
+
+
+class CertLeL2capChannel(IEventStream):
+
+    def __init__(self, device, scid, dcid, acl_stream, acl, control_channel, initial_credits=0):
+        self._device = device
+        self._scid = scid
+        self._dcid = dcid
+        self._acl_stream = acl_stream
+        self._acl = acl
+        self._control_channel = control_channel
+        self._our_acl_view = FilteringEventStream(acl_stream, L2capMatchers.ExtractBasicFrame(scid))
+        self._credits_left = initial_credits
+
+    def get_event_queue(self):
+        return self._our_acl_view.get_event_queue()
+
+    def send(self, packet):
+        frame = l2cap_packets.BasicFrameBuilder(self._dcid, packet)
+        self._acl.send(frame.Serialize())
+        self._credits_left -= 1
+
+    def send_first_le_i_frame(self, sdu_size, packet):
+        frame = l2cap_packets.FirstLeInformationFrameBuilder(self._dcid, sdu_size, packet)
+        self._acl.send(frame.Serialize())
+        self._credits_left -= 1
+
+    def disconnect_and_verify(self):
+        assertThat(self._scid).isNotEqualTo(1)
+        self._control_channel.send(l2cap_packets.LeDisconnectionRequestBuilder(1, self._dcid, self._scid))
+
+        assertThat(self._control_channel).emits(L2capMatchers.LeDisconnectionResponse(self._scid, self._dcid))
+
+    def verify_disconnect_request(self):
+        assertThat(self._control_channel).emits(L2capMatchers.LeDisconnectionRequest(self._dcid, self._scid))
+
+    def send_credits(self, num_credits):
+        self._control_channel.send(l2cap_packets.LeFlowControlCreditBuilder(2, self._scid, num_credits))
+
+    def credits_left(self):
+        return self._credits_left
+
+
+class CertLeL2cap(Closable):
+
+    def __init__(self, device):
+        self._device = device
+        self._le_acl_manager = PyLeAclManager(device)
+        self._le_acl = None
+
+        self.control_table = {
+            LeCommandCode.DISCONNECTION_REQUEST: self._on_disconnection_request_default,
+            LeCommandCode.DISCONNECTION_RESPONSE: self._on_disconnection_response_default,
+            LeCommandCode.LE_FLOW_CONTROL_CREDIT: self._on_credit,
+        }
+
+        self._cid_to_cert_channels = {}
+
+    def close(self):
+        self._le_acl_manager.close()
+        safeClose(self._le_acl)
+
+    def connect_le_acl(self, remote_addr):
+        self._le_acl = self._le_acl_manager.connect_to_remote(remote_addr)
+        self.control_channel = CertLeL2capChannel(
+            self._device, 5, 5, self._get_acl_stream(), self._le_acl, control_channel=None)
+        self._get_acl_stream().register_callback(self._handle_control_packet)
+
+    def wait_for_connection(self):
+        self._le_acl = self._le_acl_manager.wait_for_connection()
+        self.control_channel = CertLeL2capChannel(
+            self._device, 5, 5, self._get_acl_stream(), self._le_acl, control_channel=None)
+        self._get_acl_stream().register_callback(self._handle_control_packet)
+
+    def open_fixed_channel(self, cid=4):
+        channel = CertLeL2capChannel(self._device, cid, cid, self._get_acl_stream(), self._le_acl, None, 0)
+        return channel
+
+    def open_channel(self, signal_id, psm, scid, mtu=1000, mps=100, initial_credit=6):
+        self.control_channel.send(
+            l2cap_packets.LeCreditBasedConnectionRequestBuilder(signal_id, psm, scid, mtu, mps, initial_credit))
+
+        response = L2capCaptures.CreditBasedConnectionResponse()
+        assertThat(self.control_channel).emits(response)
+        channel = CertLeL2capChannel(self._device, scid,
+                                     response.get().GetDestinationCid(), self._get_acl_stream(), self._le_acl,
+                                     self.control_channel,
+                                     response.get().GetInitialCredits())
+        self._cid_to_cert_channels[scid] = channel
+        return channel
+
+    def open_channel_with_expected_result(self, psm=0x33, result=LeCreditBasedConnectionResponseResult.SUCCESS):
+        self.control_channel.send(l2cap_packets.LeCreditBasedConnectionRequestBuilder(1, psm, 0x40, 1000, 100, 6))
+
+        response = L2capMatchers.CreditBasedConnectionResponse(result)
+        assertThat(self.control_channel).emits(response)
+
+    def verify_and_respond_open_channel_from_remote(self,
+                                                    psm=0x33,
+                                                    result=LeCreditBasedConnectionResponseResult.SUCCESS,
+                                                    our_scid=None):
+        request = L2capCaptures.CreditBasedConnectionRequest(psm)
+        assertThat(self.control_channel).emits(request)
+        (scid, dcid) = self._respond_connection_request_default(request.get(), result, our_scid)
+        channel = CertLeL2capChannel(self._device, scid, dcid, self._get_acl_stream(), self._le_acl,
+                                     self.control_channel,
+                                     request.get().GetInitialCredits())
+        self._cid_to_cert_channels[scid] = channel
+        return channel
+
+    def verify_and_reject_open_channel_from_remote(self, psm=0x33):
+        request = L2capCaptures.CreditBasedConnectionRequest(psm)
+        assertThat(self.control_channel).emits(request)
+        sid = request.get().GetIdentifier()
+        reject = l2cap_packets.LeCommandRejectNotUnderstoodBuilder(sid)
+        self.control_channel.send(reject)
+
+    def verify_le_flow_control_credit(self, channel):
+        assertThat(self.control_channel).emits(L2capMatchers.LeFlowControlCredit(channel._dcid))
+
+    def _respond_connection_request_default(self,
+                                            request,
+                                            result=LeCreditBasedConnectionResponseResult.SUCCESS,
+                                            our_scid=None):
+        sid = request.GetIdentifier()
+        their_scid = request.GetSourceCid()
+        mtu = request.GetMtu()
+        mps = request.GetMps()
+        initial_credits = request.GetInitialCredits()
+        # If our_scid is not specified, we use the same value - their scid as their scid
+        if our_scid is None:
+            our_scid = their_scid
+        our_dcid = their_scid
+        response = l2cap_packets.LeCreditBasedConnectionResponseBuilder(sid, our_scid, mtu, mps, initial_credits,
+                                                                        result)
+        self.control_channel.send(response)
+        return (our_scid, our_dcid)
+
+    def get_control_channel(self):
+        return self.control_channel
+
+    def _get_acl_stream(self):
+        return self._le_acl.acl_stream
+
+    def _on_disconnection_request_default(self, request):
+        disconnection_request = l2cap_packets.LeDisconnectionRequestView(request)
+        sid = disconnection_request.GetIdentifier()
+        scid = disconnection_request.GetSourceCid()
+        dcid = disconnection_request.GetDestinationCid()
+        response = l2cap_packets.LeDisconnectionResponseBuilder(sid, dcid, scid)
+        self.control_channel.send(response)
+
+    def _on_disconnection_response_default(self, request):
+        disconnection_response = l2cap_packets.LeDisconnectionResponseView(request)
+
+    def _on_credit(self, l2cap_le_control_view):
+        credit_view = l2cap_packets.LeFlowControlCreditView(l2cap_le_control_view)
+        cid = credit_view.GetCid()
+        if cid not in self._cid_to_cert_channels:
+            return
+        self._cid_to_cert_channels[cid]._credits_left += credit_view.GetCredits()
+
+    def _handle_control_packet(self, l2cap_packet):
+        packet_bytes = l2cap_packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != 5:
+            return
+        request = l2cap_packets.LeControlView(l2cap_view.GetPayload())
+        fn = self.control_table.get(request.GetCode())
+        if fn is not None:
+            fn(request)
+        return
diff --git a/gd/l2cap/le/cert/dual_l2cap_test.py b/gd/l2cap/le/cert/dual_l2cap_test.py
new file mode 100644
index 0000000..c8bde93
--- /dev/null
+++ b/gd/l2cap/le/cert/dual_l2cap_test.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 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])
+
+
+class DualL2capTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(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)
+
+    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,
+            event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            address_type=common.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
new file mode 100644
index 0000000..96e6348
--- /dev/null
+++ b/gd/l2cap/le/cert/le_l2cap_test.py
@@ -0,0 +1,571 @@
+#
+#   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.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])
+
+
+class LeL2capTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(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)
+
+    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,
+            event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            address_type=common.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,
+            event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            address_type=common.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 slave device.
+        NOTE: This is an optional feature. Also if both LL master and slave 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 master 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 slave 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_master(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_master(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/l2cap/security_policy.h b/gd/l2cap/le/dynamic_channel.cc
similarity index 77%
copy from gd/l2cap/security_policy.h
copy to gd/l2cap/le/dynamic_channel.cc
index 5a06401..f8c3859 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/l2cap/le/dynamic_channel.cc
@@ -13,12 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
+
+#include "l2cap/le/dynamic_channel.h"
+#include "l2cap/le/internal/link.h"
 
 namespace bluetooth {
 namespace l2cap {
+namespace le {
+LinkOptions* DynamicChannel::GetLinkOptions() {
+  return link_->GetLinkOptions();
+}
 
-class SecurityPolicy {};
-
+}  // namespace le
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/dynamic_channel.h b/gd/l2cap/le/dynamic_channel.h
index db1d822..d692cf7 100644
--- a/gd/l2cap/le/dynamic_channel.h
+++ b/gd/l2cap/le/dynamic_channel.h
@@ -17,12 +17,31 @@
 #pragma once
 
 #include "l2cap/dynamic_channel.h"
+#include "l2cap/le/link_options.h"
 
 namespace bluetooth {
 namespace l2cap {
 namespace le {
+namespace internal {
+class Link;
+}
 
-using DynamicChannel = l2cap::DynamicChannel;
+class DynamicChannel : public l2cap::DynamicChannel {
+ public:
+  DynamicChannel(std::shared_ptr<l2cap::internal::DynamicChannelImpl> impl, os::Handler* l2cap_handler,
+                 internal::Link* link)
+      : l2cap::DynamicChannel(impl, l2cap_handler), link_(link) {}
+
+  /**
+   * Get the Proxy for L2CAP Link Options.
+   * Only few special L2CAP users need to use it, including
+   * Hearing Aid Profile and Java API.
+   */
+  LinkOptions* GetLinkOptions();
+
+ private:
+  internal::Link* link_;
+};
 
 }  // namespace le
 }  // namespace l2cap
diff --git a/gd/l2cap/le/dynamic_channel_manager.cc b/gd/l2cap/le/dynamic_channel_manager.cc
index b8303c1..677e92c 100644
--- a/gd/l2cap/le/dynamic_channel_manager.cc
+++ b/gd/l2cap/le/dynamic_channel_manager.cc
@@ -47,6 +47,7 @@
                                             OnConnectionOpenCallback on_connection_open, os::Handler* handler) {
   internal::DynamicChannelServiceImpl::PendingRegistration pending_registration{
       .user_handler_ = handler,
+      .security_policy_ = security_policy,
       .on_registration_complete_callback_ = std::move(on_registration_complete),
       .on_connection_open_callback_ = std::move(on_connection_open),
       .configuration_ = configuration_option,
diff --git a/gd/l2cap/le/dynamic_channel_manager.h b/gd/l2cap/le/dynamic_channel_manager.h
index b57ec2c..e5334ef 100644
--- a/gd/l2cap/le/dynamic_channel_manager.h
+++ b/gd/l2cap/le/dynamic_channel_manager.h
@@ -23,8 +23,8 @@
 #include "l2cap/le/dynamic_channel.h"
 #include "l2cap/le/dynamic_channel_configuration_option.h"
 #include "l2cap/le/dynamic_channel_service.h"
+#include "l2cap/le/security_policy.h"
 #include "l2cap/psm.h"
-#include "l2cap/security_policy.h"
 #include "os/handler.h"
 
 namespace bluetooth {
@@ -50,7 +50,8 @@
   struct ConnectionResult {
     ConnectionResultCode connection_result_code = ConnectionResultCode::SUCCESS;
     hci::ErrorCode hci_error = hci::ErrorCode::SUCCESS;
-    ConnectionResponseResult l2cap_connection_response_result = ConnectionResponseResult::SUCCESS;
+    LeCreditBasedConnectionResponseResult l2cap_connection_response_result =
+        LeCreditBasedConnectionResponseResult::SUCCESS;
   };
   /**
    * OnConnectionFailureCallback(std::string failure_reason);
diff --git a/gd/l2cap/le/dynamic_channel_service.h b/gd/l2cap/le/dynamic_channel_service.h
index e7341df..408a20c 100644
--- a/gd/l2cap/le/dynamic_channel_service.h
+++ b/gd/l2cap/le/dynamic_channel_service.h
@@ -47,13 +47,15 @@
 
   Psm GetPsm() const;
 
- private:
+ protected:
   DynamicChannelService(Psm psm, internal::DynamicChannelServiceManagerImpl* manager, os::Handler* handler)
       : psm_(psm), manager_(manager), l2cap_layer_handler_(handler) {
     ASSERT(IsPsmValid(psm));
     ASSERT(manager_ != nullptr);
     ASSERT(l2cap_layer_handler_ != nullptr);
   }
+
+ private:
   Psm psm_ = kDefaultPsm;
   internal::DynamicChannelServiceManagerImpl* manager_ = nullptr;
   os::Handler* l2cap_layer_handler_;
diff --git a/gd/l2cap/le/facade.cc b/gd/l2cap/le/facade.cc
new file mode 100644
index 0000000..83c2fc7
--- /dev/null
+++ b/gd/l2cap/le/facade.cc
@@ -0,0 +1,445 @@
+/*
+ * 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.
+ */
+
+#include "l2cap/le/facade.h"
+
+#include "grpc/grpc_event_queue.h"
+#include "l2cap/le/dynamic_channel.h"
+#include "l2cap/le/dynamic_channel_manager.h"
+#include "l2cap/le/dynamic_channel_service.h"
+#include "l2cap/le/facade.grpc.pb.h"
+#include "l2cap/le/l2cap_le_module.h"
+#include "l2cap/le/security_policy.h"
+#include "l2cap/psm.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+SecurityPolicy SecurityLevelToPolicy(SecurityLevel level) {
+  switch (level) {
+    case SecurityLevel::NO_SECURITY:
+      return SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK;
+    case SecurityLevel::UNAUTHENTICATED_PAIRING_WITH_ENCRYPTION:
+      return SecurityPolicy::ENCRYPTED_TRANSPORT;
+    case SecurityLevel::AUTHENTICATED_PAIRING_WITH_ENCRYPTION:
+      return SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT;
+    case SecurityLevel::AUTHENTICATED_PAIRING_WITH_128_BIT_KEY:
+      return SecurityPolicy::_NOT_FOR_YOU__AUTHENTICATED_PAIRING_WITH_128_BIT_KEY;
+    case SecurityLevel::AUTHORIZATION:
+      return SecurityPolicy::_NOT_FOR_YOU__AUTHORIZATION;
+    default:
+      return SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK;
+  }
+}
+
+class L2capLeModuleFacadeService : public L2capLeModuleFacade::Service {
+ public:
+  L2capLeModuleFacadeService(L2capLeModule* l2cap_layer, os::Handler* facade_handler)
+      : l2cap_layer_(l2cap_layer), facade_handler_(facade_handler) {
+    ASSERT(l2cap_layer_ != nullptr);
+    ASSERT(facade_handler_ != nullptr);
+  }
+
+  ::grpc::Status FetchL2capData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                ::grpc::ServerWriter<::bluetooth::l2cap::le::L2capPacket>* writer) override {
+    return pending_l2cap_data_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status OpenDynamicChannel(::grpc::ServerContext* context, const OpenDynamicChannelRequest* request,
+                                    OpenDynamicChannelResponse* response) override {
+    auto service_helper = dynamic_channel_helper_map_.find(request->psm());
+    if (service_helper == dynamic_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+    }
+    hci::Address peer_address;
+    ASSERT(hci::Address::FromString(request->remote().address().address(), peer_address));
+    // TODO: Support different address type
+    hci::AddressWithType peer(peer_address, hci::AddressType::RANDOM_DEVICE_ADDRESS);
+    service_helper->second->Connect(peer);
+    response->set_status(
+        static_cast<int>(service_helper->second->channel_open_fail_reason_.l2cap_connection_response_result));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status CloseDynamicChannel(::grpc::ServerContext* context, const CloseDynamicChannelRequest* request,
+                                     ::google::protobuf::Empty* response) override {
+    auto service_helper = dynamic_channel_helper_map_.find(request->psm());
+    if (service_helper == dynamic_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+    }
+    if (service_helper->second->channel_ == nullptr) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
+    }
+    auto address = service_helper->second->channel_->GetDevice().GetAddress();
+    hci::Address peer_address;
+    ASSERT(hci::Address::FromString(request->remote().address().address(), peer_address));
+    if (address != peer_address) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Remote address doesn't match");
+    }
+    service_helper->second->channel_->Close();
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetDynamicChannel(::grpc::ServerContext* context,
+                                   const ::bluetooth::l2cap::le::SetEnableDynamicChannelRequest* request,
+                                   ::google::protobuf::Empty* response) override {
+    if (request->enable()) {
+      dynamic_channel_helper_map_.emplace(request->psm(), std::make_unique<L2capDynamicChannelHelper>(
+                                                              this, l2cap_layer_, facade_handler_, request->psm(),
+                                                              SecurityLevelToPolicy(request->security_level())));
+      return ::grpc::Status::OK;
+    } else {
+      auto service_helper = dynamic_channel_helper_map_.find(request->psm());
+      if (service_helper == dynamic_channel_helper_map_.end()) {
+        return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+      }
+      service_helper->second->service_->Unregister(common::BindOnce([] {}), facade_handler_);
+      return ::grpc::Status::OK;
+    }
+  }
+
+  ::grpc::Status SendDynamicChannelPacket(::grpc::ServerContext* context,
+                                          const ::bluetooth::l2cap::le::DynamicChannelPacket* request,
+                                          ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(channel_map_mutex_);
+    if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+    }
+    std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
+    if (!dynamic_channel_helper_map_[request->psm()]->SendPacket(packet)) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
+    }
+    return ::grpc::Status::OK;
+  }
+
+  class L2capDynamicChannelHelper {
+   public:
+    L2capDynamicChannelHelper(L2capLeModuleFacadeService* service, L2capLeModule* l2cap_layer, os::Handler* handler,
+                              Psm psm, SecurityPolicy security_policy)
+        : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm) {
+      dynamic_channel_manager_ = l2cap_layer_->GetDynamicChannelManager();
+      dynamic_channel_manager_->RegisterService(
+          psm, {}, security_policy,
+          common::BindOnce(&L2capDynamicChannelHelper::on_l2cap_service_registration_complete,
+                           common::Unretained(this)),
+          common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)), handler_);
+    }
+
+    ~L2capDynamicChannelHelper() {
+      if (channel_ != nullptr) {
+        channel_->GetQueueUpEnd()->UnregisterDequeue();
+        channel_ = nullptr;
+      }
+    }
+
+    void Connect(hci::AddressWithType address) {
+      dynamic_channel_manager_->ConnectChannel(
+          address, {}, psm_, common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)),
+          common::Bind(&L2capDynamicChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
+      std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+      if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
+        LOG_WARN("Channel is not open for psm %d", psm_);
+      }
+    }
+
+    void on_l2cap_service_registration_complete(DynamicChannelManager::RegistrationResult registration_result,
+                                                std::unique_ptr<DynamicChannelService> service) {
+      if (registration_result != DynamicChannelManager::RegistrationResult::SUCCESS) {
+        LOG_ERROR("Service registration failed");
+      } else {
+        service_ = std::move(service);
+      }
+    }
+
+    // invoked from Facade Handler
+    void on_connection_open(std::unique_ptr<DynamicChannel> channel) {
+      {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        channel_ = std::move(channel);
+      }
+      channel_open_cv_.notify_all();
+      channel_->RegisterOnCloseCallback(
+          facade_service_->facade_handler_->BindOnceOn(this, &L2capDynamicChannelHelper::on_close_callback));
+      channel_->GetQueueUpEnd()->RegisterDequeue(
+          facade_service_->facade_handler_,
+          common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
+    }
+
+    void on_close_callback(hci::ErrorCode error_code) {
+      {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        channel_->GetQueueUpEnd()->UnregisterDequeue();
+      }
+      channel_ = nullptr;
+    }
+
+    void on_connect_fail(DynamicChannelManager::ConnectionResult result) {
+      {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        channel_ = nullptr;
+        channel_open_fail_reason_ = result;
+      }
+      channel_open_cv_.notify_all();
+    }
+
+    void on_incoming_packet() {
+      auto packet = channel_->GetQueueUpEnd()->TryDequeue();
+      std::string data = std::string(packet->begin(), packet->end());
+      L2capPacket l2cap_data;
+      l2cap_data.set_psm(psm_);
+      l2cap_data.set_payload(data);
+      facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
+    }
+
+    bool SendPacket(std::vector<uint8_t> packet) {
+      if (channel_ == nullptr) {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
+          LOG_WARN("Channel is not open for psm %d", psm_);
+          return false;
+        }
+      }
+      std::promise<void> promise;
+      auto future = promise.get_future();
+      channel_->GetQueueUpEnd()->RegisterEnqueue(
+          handler_, common::Bind(&L2capDynamicChannelHelper::enqueue_callback, common::Unretained(this), packet,
+                                 common::Passed(std::move(promise))));
+      auto status = future.wait_for(std::chrono::milliseconds(500));
+      if (status != std::future_status::ready) {
+        LOG_ERROR("Can't send packet because the previous packet wasn't sent yet");
+        return false;
+      }
+      return true;
+    }
+
+    std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet,
+                                                                std::promise<void> promise) {
+      auto packet_one = std::make_unique<packet::RawBuilder>(2000);
+      packet_one->AddOctets(packet);
+      channel_->GetQueueUpEnd()->UnregisterEnqueue();
+      promise.set_value();
+      return packet_one;
+    }
+
+    L2capLeModuleFacadeService* facade_service_;
+    L2capLeModule* l2cap_layer_;
+    os::Handler* handler_;
+    std::unique_ptr<DynamicChannelManager> dynamic_channel_manager_;
+    std::unique_ptr<DynamicChannelService> service_;
+    std::unique_ptr<DynamicChannel> channel_ = nullptr;
+    Psm psm_;
+    DynamicChannelManager::ConnectionResult channel_open_fail_reason_;
+    std::condition_variable channel_open_cv_;
+    std::mutex channel_open_cv_mutex_;
+  };
+
+  ::grpc::Status SetFixedChannel(::grpc::ServerContext* context, const SetEnableFixedChannelRequest* request,
+                                 ::google::protobuf::Empty* response) override {
+    if (request->enable()) {
+      fixed_channel_helper_map_.emplace(request->cid(), std::make_unique<L2capFixedChannelHelper>(
+                                                            this, l2cap_layer_, facade_handler_, request->cid()));
+      return ::grpc::Status::OK;
+    } else {
+      auto service_helper = fixed_channel_helper_map_.find(request->cid());
+      if (service_helper == fixed_channel_helper_map_.end()) {
+        return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Cid not registered");
+      }
+      service_helper->second->channel_->Release();
+      service_helper->second->service_->Unregister(common::BindOnce([] {}), facade_handler_);
+      return ::grpc::Status::OK;
+    }
+  }
+
+  ::grpc::Status SendFixedChannelPacket(::grpc::ServerContext* context, const FixedChannelPacket* request,
+                                        ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(channel_map_mutex_);
+    if (fixed_channel_helper_map_.find(request->cid()) == fixed_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Cid not registered");
+    }
+    std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
+    if (!fixed_channel_helper_map_[request->cid()]->SendPacket(packet)) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
+    }
+    return ::grpc::Status::OK;
+  }
+
+  class L2capFixedChannelHelper {
+   public:
+    L2capFixedChannelHelper(L2capLeModuleFacadeService* service, L2capLeModule* l2cap_layer, os::Handler* handler,
+                            Cid cid)
+        : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), cid_(cid) {
+      fixed_channel_manager_ = l2cap_layer_->GetFixedChannelManager();
+      fixed_channel_manager_->RegisterService(
+          cid_,
+          common::BindOnce(&L2capFixedChannelHelper::on_l2cap_service_registration_complete, common::Unretained(this)),
+          common::Bind(&L2capFixedChannelHelper::on_connection_open, common::Unretained(this)), handler_);
+    }
+
+    ~L2capFixedChannelHelper() {
+      if (channel_ != nullptr) {
+        channel_->GetQueueUpEnd()->UnregisterDequeue();
+        channel_->Release();
+        channel_ = nullptr;
+      }
+    }
+
+    void Connect(hci::AddressWithType address) {
+      fixed_channel_manager_->ConnectServices(
+          address, common::BindOnce(&L2capFixedChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
+      std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+      if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
+        LOG_WARN("Channel is not open for cid %d", cid_);
+      }
+    }
+
+    void on_l2cap_service_registration_complete(FixedChannelManager::RegistrationResult registration_result,
+                                                std::unique_ptr<FixedChannelService> service) {
+      if (registration_result != FixedChannelManager::RegistrationResult::SUCCESS) {
+        LOG_ERROR("Service registration failed");
+      } else {
+        service_ = std::move(service);
+      }
+    }
+
+    // invoked from Facade Handler
+    void on_connection_open(std::unique_ptr<FixedChannel> channel) {
+      {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        channel_ = std::move(channel);
+        channel_->RegisterOnCloseCallback(
+            handler_, common::BindOnce(&L2capFixedChannelHelper::on_close_callback, common::Unretained(this)));
+        channel_->Acquire();
+      }
+      channel_open_cv_.notify_all();
+      channel_->GetQueueUpEnd()->RegisterDequeue(
+          facade_service_->facade_handler_,
+          common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(this)));
+    }
+
+    void on_close_callback(hci::ErrorCode error_code) {
+      {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        channel_->GetQueueUpEnd()->UnregisterDequeue();
+      }
+      channel_ = nullptr;
+    }
+
+    void on_connect_fail(FixedChannelManager::ConnectionResult result) {
+      {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        channel_ = nullptr;
+      }
+      channel_open_cv_.notify_all();
+    }
+
+    void on_incoming_packet() {
+      auto packet = channel_->GetQueueUpEnd()->TryDequeue();
+      std::string data = std::string(packet->begin(), packet->end());
+      L2capPacket l2cap_data;
+      l2cap_data.set_fixed_cid(cid_);
+      l2cap_data.set_payload(data);
+      facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
+    }
+
+    bool SendPacket(std::vector<uint8_t> packet) {
+      if (channel_ == nullptr) {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(2), [this] { return channel_ != nullptr; })) {
+          LOG_WARN("Channel is not open for cid %d", cid_);
+          return false;
+        }
+      }
+      std::promise<void> promise;
+      auto future = promise.get_future();
+      channel_->GetQueueUpEnd()->RegisterEnqueue(
+          handler_, common::Bind(&L2capFixedChannelHelper::enqueue_callback, common::Unretained(this), packet,
+                                 common::Passed(std::move(promise))));
+      auto status = future.wait_for(std::chrono::milliseconds(500));
+      if (status != std::future_status::ready) {
+        LOG_ERROR("Can't send packet because the previous packet wasn't sent yet");
+        return false;
+      }
+      return true;
+    }
+
+    std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet,
+                                                                std::promise<void> promise) {
+      auto packet_one = std::make_unique<packet::RawBuilder>(2000);
+      packet_one->AddOctets(packet);
+      channel_->GetQueueUpEnd()->UnregisterEnqueue();
+      promise.set_value();
+      return packet_one;
+    }
+
+    L2capLeModuleFacadeService* facade_service_;
+    L2capLeModule* l2cap_layer_;
+    os::Handler* handler_;
+    std::unique_ptr<FixedChannelManager> fixed_channel_manager_;
+    std::unique_ptr<FixedChannelService> service_;
+    std::unique_ptr<FixedChannel> channel_ = nullptr;
+    Cid cid_;
+    std::condition_variable channel_open_cv_;
+    std::mutex channel_open_cv_mutex_;
+  };
+
+  ::grpc::Status SendConnectionParameterUpdate(::grpc::ServerContext* context, const ConnectionParameter* request,
+                                               ::google::protobuf::Empty* response) override {
+    if (dynamic_channel_helper_map_.empty()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Need to open at least one dynamic channel first");
+    }
+    auto& dynamic_channel_helper = dynamic_channel_helper_map_.begin()->second;
+    dynamic_channel_helper->channel_->GetLinkOptions()->UpdateConnectionParameter(
+        request->conn_interval_min(), request->conn_interval_max(), request->conn_latency(),
+        request->supervision_timeout(), request->min_ce_length(), request->max_ce_length());
+
+    return ::grpc::Status::OK;
+  }
+
+  L2capLeModule* l2cap_layer_;
+  os::Handler* facade_handler_;
+  std::mutex channel_map_mutex_;
+  std::map<Psm, std::unique_ptr<L2capDynamicChannelHelper>> dynamic_channel_helper_map_;
+  std::map<Cid, std::unique_ptr<L2capFixedChannelHelper>> fixed_channel_helper_map_;
+  ::bluetooth::grpc::GrpcEventQueue<L2capPacket> pending_l2cap_data_{"FetchL2capData"};
+};
+
+void L2capLeModuleFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<l2cap::le::L2capLeModule>();
+}
+
+void L2capLeModuleFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new L2capLeModuleFacadeService(GetDependency<l2cap::le::L2capLeModule>(), GetHandler());
+}
+
+void L2capLeModuleFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* L2capLeModuleFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory L2capLeModuleFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new L2capLeModuleFacadeModule(); });
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/hci/cert/cert.h b/gd/l2cap/le/facade.h
similarity index 75%
copy from gd/hci/cert/cert.h
copy to gd/l2cap/le/facade.h
index e3ecf46..0f811c8 100644
--- a/gd/hci/cert/cert.h
+++ b/gd/l2cap/le/facade.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,33 +13,34 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #pragma once
 
 #include <grpc++/grpc++.h>
 
 #include "grpc/grpc_module.h"
-#include "hci/acl_manager.h"
 
 namespace bluetooth {
-namespace hci {
-namespace cert {
+namespace l2cap {
+namespace le {
 
-class AclManagerCertService;
+class L2capLeModuleFacadeService;
 
-class AclManagerCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class L2capLeModuleFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
   void ListDependencies(ModuleList* list) override;
+
   void Start() override;
+
   void Stop() override;
+
   ::grpc::Service* GetService() const override;
 
  private:
-  AclManagerCertService* service_;
+  L2capLeModuleFacadeService* service_;
 };
 
-}  // namespace cert
-}  // namespace hci
+}  // namespace le
+}  // namespace l2cap
 }  // namespace bluetooth
diff --git a/gd/l2cap/le/facade.proto b/gd/l2cap/le/facade.proto
new file mode 100644
index 0000000..b9b4841
--- /dev/null
+++ b/gd/l2cap/le/facade.proto
@@ -0,0 +1,85 @@
+syntax = "proto3";
+
+package bluetooth.l2cap.le;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service L2capLeModuleFacade {
+  rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {}
+  // Initiate a credit based connection request and block until response is received for up to some timeout (2s)
+  rpc OpenDynamicChannel(OpenDynamicChannelRequest) returns (OpenDynamicChannelResponse) {}
+  rpc CloseDynamicChannel(CloseDynamicChannelRequest) returns (google.protobuf.Empty) {}
+  rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {}
+  rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {}
+  rpc SetFixedChannel(SetEnableFixedChannelRequest) returns (google.protobuf.Empty) {}
+  rpc SendFixedChannelPacket(FixedChannelPacket) returns (google.protobuf.Empty) {}
+  rpc SendConnectionParameterUpdate(ConnectionParameter) returns (google.protobuf.Empty) {}
+}
+
+message L2capPacket {
+  oneof channel_type {
+    uint32 psm = 1;
+    uint32 fixed_cid = 2;
+  }
+  bytes payload = 3;
+}
+
+message DynamicChannelOpenEvent {
+  uint32 psm = 1;
+  uint32 connection_response_result = 2;
+}
+
+message OpenDynamicChannelRequest {
+  facade.BluetoothAddressWithType remote = 1;
+  uint32 psm = 2;
+}
+
+message OpenDynamicChannelResponse {
+  uint32 status = 1;
+}
+
+message CloseDynamicChannelRequest {
+  facade.BluetoothAddressWithType remote = 1;
+  uint32 psm = 2;
+}
+
+enum SecurityLevel {
+  NO_SECURITY = 0;
+  UNAUTHENTICATED_PAIRING_WITH_ENCRYPTION = 1;
+  AUTHENTICATED_PAIRING_WITH_ENCRYPTION = 2;
+  AUTHENTICATED_PAIRING_WITH_128_BIT_KEY = 3;
+  AUTHORIZATION = 4;
+}
+
+message SetEnableDynamicChannelRequest {
+  uint32 psm = 1;
+  bool enable = 2;
+  SecurityLevel security_level = 3;
+}
+
+message DynamicChannelPacket {
+  facade.BluetoothAddressWithType remote = 1;
+  uint32 psm = 2;
+  bytes payload = 3;
+}
+
+message SetEnableFixedChannelRequest {
+  uint32 cid = 1;
+  bool enable = 2;
+}
+
+message FixedChannelPacket {
+  facade.BluetoothAddressWithType remote = 1;
+  uint32 cid = 2;
+  bytes payload = 3;
+}
+
+message ConnectionParameter {
+  uint32 conn_interval_min = 2;
+  uint32 conn_interval_max = 3;
+  uint32 conn_latency = 4;
+  uint32 supervision_timeout = 5;
+  uint32 min_ce_length = 6;
+  uint32 max_ce_length = 7;
+}
diff --git a/gd/l2cap/le/fixed_channel.cc b/gd/l2cap/le/fixed_channel.cc
index d02dad6..fb3fd4c 100644
--- a/gd/l2cap/le/fixed_channel.cc
+++ b/gd/l2cap/le/fixed_channel.cc
@@ -26,10 +26,6 @@
   return impl_->GetDevice();
 }
 
-hci::Role FixedChannel::GetRole() const {
-  return impl_->GetRole();
-}
-
 void FixedChannel::RegisterOnCloseCallback(os::Handler* user_handler, FixedChannel::OnCloseCallback on_close_callback) {
   l2cap_handler_->Post(common::BindOnce(&internal::FixedChannelImpl::RegisterOnCloseCallback, impl_, user_handler,
                                         std::move(on_close_callback)));
@@ -47,6 +43,11 @@
 FixedChannel::GetQueueUpEnd() const {
   return impl_->GetQueueUpEnd();
 }
+
+LinkOptions* FixedChannel::GetLinkOptions() {
+  return impl_->GetLinkOptions();
+}
+
 }  // namespace le
 }  // namespace l2cap
 }  // namespace bluetooth
diff --git a/gd/l2cap/le/fixed_channel.h b/gd/l2cap/le/fixed_channel.h
index ad4674b..7a7cc94 100644
--- a/gd/l2cap/le/fixed_channel.h
+++ b/gd/l2cap/le/fixed_channel.h
@@ -19,6 +19,7 @@
 #include "common/callback.h"
 #include "hci/acl_manager.h"
 #include "l2cap/cid.h"
+#include "l2cap/le/link_options.h"
 #include "os/handler.h"
 #include "packet/base_packet_builder.h"
 #include "packet/packet_view.h"
@@ -49,11 +50,6 @@
   hci::AddressWithType GetDevice() const;
 
   /**
-   * Return the role we have in the associated link
-   */
-  hci::Role GetRole() const;
-
-  /**
    * Register close callback. If close callback is registered, when a channel is closed, the channel's resource will
    * only be freed after on_close callback is invoked. Otherwise, if no on_close callback is registered, the channel's
    * resource will be freed immediately after closing.
@@ -85,6 +81,13 @@
    */
   common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>* GetQueueUpEnd() const;
 
+  /**
+   * Get the Proxy for L2CAP Link Options.
+   * Only few special L2CAP users need to use it, including
+   * GATT, HID Device, Security Manager, and Java API.
+   */
+  LinkOptions* GetLinkOptions();
+
  private:
   std::shared_ptr<internal::FixedChannelImpl> impl_;
   os::Handler* l2cap_handler_;
diff --git a/gd/l2cap/le/fixed_channel_manager.cc b/gd/l2cap/le/fixed_channel_manager.cc
index e36a7b4..c3c4763 100644
--- a/gd/l2cap/le/fixed_channel_manager.cc
+++ b/gd/l2cap/le/fixed_channel_manager.cc
@@ -35,8 +35,7 @@
   return true;
 }
 
-bool FixedChannelManager::RegisterService(Cid cid, const SecurityPolicy& security_policy,
-                                          OnRegistrationCompleteCallback on_registration_complete,
+bool FixedChannelManager::RegisterService(Cid cid, OnRegistrationCompleteCallback on_registration_complete,
                                           OnConnectionOpenCallback on_connection_open, os::Handler* handler) {
   internal::FixedChannelServiceImpl::PendingRegistration pending_registration{
       .user_handler_ = handler,
diff --git a/gd/l2cap/le/fixed_channel_manager.h b/gd/l2cap/le/fixed_channel_manager.h
index 4583310..1b059ac 100644
--- a/gd/l2cap/le/fixed_channel_manager.h
+++ b/gd/l2cap/le/fixed_channel_manager.h
@@ -17,12 +17,10 @@
 
 #include <string>
 
-#include "hci/acl_manager.h"
 #include "hci/address_with_type.h"
 #include "l2cap/cid.h"
 #include "l2cap/le/fixed_channel.h"
 #include "l2cap/le/fixed_channel_service.h"
-#include "l2cap/security_policy.h"
 #include "os/handler.h"
 
 namespace bluetooth {
@@ -115,19 +113,17 @@
    *   FixedChannelService object. The registered service can be managed from that object.
    * - If a CID is already registered or some other error happens, on_registration_complete will be triggered with a
    *   non-SUCCESS value
-   * - After a service is registered, any classic ACL connection will create a FixedChannel object that is
+   * - After a service is registered, any LE ACL connection will create a FixedChannel object that is
    *   delivered through on_open_callback
    * - on_open_callback, will only be triggered after on_service_registered callback
    *
    * @param cid:  cid used to receive incoming connections
-   * @param security_policy: The security policy used for the connection.
    * @param on_registration_complete: A callback to indicate the service setup has completed. If the return status is
    *        not SUCCESS, it means service is not registered due to reasons like CID already take
    * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
    * @param handler: The handler context in which to execute the @callback parameter.
    */
-  bool RegisterService(Cid cid, const SecurityPolicy& security_policy,
-                       OnRegistrationCompleteCallback on_registration_complete,
+  bool RegisterService(Cid cid, OnRegistrationCompleteCallback on_registration_complete,
                        OnConnectionOpenCallback on_connection_open, os::Handler* handler);
 
   friend class L2capLeModule;
diff --git a/gd/l2cap/le/internal/dynamic_channel_service_impl.h b/gd/l2cap/le/internal/dynamic_channel_service_impl.h
index 101ef62..25af094 100644
--- a/gd/l2cap/le/internal/dynamic_channel_service_impl.h
+++ b/gd/l2cap/le/internal/dynamic_channel_service_impl.h
@@ -22,6 +22,7 @@
 #include "l2cap/le/dynamic_channel_configuration_option.h"
 #include "l2cap/le/dynamic_channel_manager.h"
 #include "l2cap/le/dynamic_channel_service.h"
+#include "l2cap/le/security_policy.h"
 
 namespace bluetooth {
 namespace l2cap {
@@ -33,6 +34,7 @@
 
   struct PendingRegistration {
     os::Handler* user_handler_ = nullptr;
+    SecurityPolicy security_policy_;
     DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete_callback_;
     DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
     DynamicChannelConfigurationOption configuration_;
@@ -46,20 +48,25 @@
     return config_option_;
   }
 
+  SecurityPolicy GetSecurityPolicy() {
+    return security_policy_;
+  }
+
   friend class DynamicChannelServiceManagerImpl;
 
  protected:
   // protected access for mocking
   DynamicChannelServiceImpl(os::Handler* user_handler,
                             DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback,
-                            DynamicChannelConfigurationOption config_option)
+                            DynamicChannelConfigurationOption config_option, SecurityPolicy security_policy)
       : user_handler_(user_handler), on_connection_open_callback_(std::move(on_connection_open_callback)),
-        config_option_(config_option) {}
+        config_option_(config_option), security_policy_(security_policy) {}
 
  private:
   os::Handler* user_handler_ = nullptr;
   DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
   DynamicChannelConfigurationOption config_option_;
+  SecurityPolicy security_policy_;
 };
 
 }  // namespace internal
diff --git a/gd/l2cap/le/internal/dynamic_channel_service_impl_mock.h b/gd/l2cap/le/internal/dynamic_channel_service_impl_mock.h
new file mode 100644
index 0000000..4edfee8
--- /dev/null
+++ b/gd/l2cap/le/internal/dynamic_channel_service_impl_mock.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include "l2cap/le/internal/dynamic_channel_service_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+namespace testing {
+
+class MockDynamicChannelServiceImpl : public DynamicChannelServiceImpl {
+ public:
+  MOCK_METHOD(SecurityPolicy, GetSecurityPolicy, (), (const, override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.cc b/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.cc
index ca3381b..2c715ca 100644
--- a/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.cc
+++ b/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.cc
@@ -39,10 +39,10 @@
         std::move(pending_registration.on_registration_complete_callback_),
         DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE, std::move(invalid_service)));
   } else {
-    service_map_.try_emplace(psm,
-                             DynamicChannelServiceImpl(pending_registration.user_handler_,
-                                                       std::move(pending_registration.on_connection_open_callback_),
-                                                       pending_registration.configuration_));
+    service_map_.try_emplace(
+        psm, DynamicChannelServiceImpl(pending_registration.user_handler_,
+                                       std::move(pending_registration.on_connection_open_callback_),
+                                       pending_registration.configuration_, pending_registration.security_policy_));
     std::unique_ptr<DynamicChannelService> user_service(new DynamicChannelService(psm, this, l2cap_layer_handler_));
     pending_registration.user_handler_->Post(
         common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
diff --git a/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.h b/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.h
index 5de5f80..169853f 100644
--- a/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.h
+++ b/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.h
@@ -20,6 +20,7 @@
 
 #include "l2cap/le/dynamic_channel_service.h"
 #include "l2cap/le/internal/dynamic_channel_service_impl.h"
+#include "l2cap/le/security_enforcement_interface.h"
 #include "l2cap/psm.h"
 #include "os/handler.h"
 
@@ -44,9 +45,19 @@
 
   virtual std::vector<std::pair<Psm, DynamicChannelServiceImpl*>> GetRegisteredServices();
 
+  // Implementation is set by SecurityManager through L2capModule
+  void SetSecurityEnforcementInterface(SecurityEnforcementInterface* impl) {
+    security_enforcement_interface_ = impl;
+  }
+
+  SecurityEnforcementInterface* GetSecurityEnforcementInterface() {
+    return security_enforcement_interface_;
+  }
+
  private:
   os::Handler* l2cap_layer_handler_ = nullptr;
   std::unordered_map<Psm, DynamicChannelServiceImpl> service_map_;
+  SecurityEnforcementInterface* security_enforcement_interface_;
 };
 }  // namespace internal
 }  // namespace le
diff --git a/gd/l2cap/le/internal/dynamic_channel_service_manager_test.cc b/gd/l2cap/le/internal/dynamic_channel_service_manager_test.cc
index 00d66d1..25ea136 100644
--- a/gd/l2cap/le/internal/dynamic_channel_service_manager_test.cc
+++ b/gd/l2cap/le/internal/dynamic_channel_service_manager_test.cc
@@ -76,6 +76,7 @@
 TEST_F(L2capLeDynamicServiceManagerTest, register_and_unregister_le_dynamic_channel) {
   DynamicChannelServiceImpl::PendingRegistration pending_registration{
       .user_handler_ = user_handler_,
+      .security_policy_ = SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK,
       .on_registration_complete_callback_ =
           common::BindOnce(&L2capLeDynamicServiceManagerTest::OnServiceRegistered, common::Unretained(this), true)};
   Cid cid = kSmpBrCid;
@@ -91,6 +92,7 @@
 TEST_F(L2capLeDynamicServiceManagerTest, register_le_dynamic_channel_bad_cid) {
   DynamicChannelServiceImpl::PendingRegistration pending_registration{
       .user_handler_ = user_handler_,
+      .security_policy_ = SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK,
       .on_registration_complete_callback_ =
           common::BindOnce(&L2capLeDynamicServiceManagerTest::OnServiceRegistered, common::Unretained(this), false)};
   Cid cid = 0x1000;
diff --git a/gd/l2cap/le/internal/fixed_channel_impl.cc b/gd/l2cap/le/internal/fixed_channel_impl.cc
index 6e254c8..3adeb5f 100644
--- a/gd/l2cap/le/internal/fixed_channel_impl.cc
+++ b/gd/l2cap/le/internal/fixed_channel_impl.cc
@@ -16,10 +16,10 @@
 
 #include <unordered_map>
 
+#include "hci/acl_manager/le_acl_connection.h"
 #include "l2cap/cid.h"
 #include "l2cap/le/internal/fixed_channel_impl.h"
 #include "l2cap/le/internal/link.h"
-#include "l2cap/security_policy.h"
 #include "os/handler.h"
 #include "os/log.h"
 
@@ -32,6 +32,10 @@
   return link_->GetRole();
 }
 
+hci::acl_manager::LeAclConnection* FixedChannelImpl::GetAclConnection() const {
+  return link_->GetAclConnection();
+}
+
 FixedChannelImpl::FixedChannelImpl(Cid cid, Link* link, os::Handler* l2cap_handler)
     : cid_(cid), device_(link->GetDevice()), link_(link), l2cap_handler_(l2cap_handler) {
   ASSERT_LOG(cid_ >= kFirstFixedChannel && cid_ <= kLastFixedChannel, "Invalid cid: %d", cid_);
@@ -106,6 +110,10 @@
   return cid_;
 }
 
+LinkOptions* FixedChannelImpl::GetLinkOptions() {
+  return link_->GetLinkOptions();
+}
+
 }  // namespace internal
 }  // namespace le
 }  // namespace l2cap
diff --git a/gd/l2cap/le/internal/fixed_channel_impl.h b/gd/l2cap/le/internal/fixed_channel_impl.h
index 5d6a11d..d4fb3a3 100644
--- a/gd/l2cap/le/internal/fixed_channel_impl.h
+++ b/gd/l2cap/le/internal/fixed_channel_impl.h
@@ -17,9 +17,11 @@
 #pragma once
 
 #include "common/bidi_queue.h"
+#include "hci/acl_manager/le_acl_connection.h"
 #include "l2cap/cid.h"
 #include "l2cap/internal/channel_impl.h"
 #include "l2cap/le/fixed_channel.h"
+#include "l2cap/le/link_options.h"
 #include "os/handler.h"
 #include "os/log.h"
 
@@ -43,6 +45,8 @@
   /* Return the role we have in the associated link */
   virtual hci::Role GetRole() const;
 
+  virtual hci::acl_manager::LeAclConnection* GetAclConnection() const;
+
   virtual void RegisterOnCloseCallback(os::Handler* user_handler, FixedChannel::OnCloseCallback on_close_callback);
 
   virtual void Acquire();
@@ -71,6 +75,8 @@
     return channel_queue_.GetDownEnd();
   }
 
+  LinkOptions* GetLinkOptions();
+
  private:
   // Constructor states
   // For logging purpose only
diff --git a/gd/l2cap/le/internal/fixed_channel_impl_test.cc b/gd/l2cap/le/internal/fixed_channel_impl_test.cc
index f88efac..9b5f276 100644
--- a/gd/l2cap/le/internal/fixed_channel_impl_test.cc
+++ b/gd/l2cap/le/internal/fixed_channel_impl_test.cc
@@ -62,7 +62,11 @@
 
 TEST_F(L2capLeFixedChannelImplTest, get_device) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockLeAclConnection* mock_acl_connection = new testing::MockLeAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetRemoteAddress()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockLeAclConnection>(mock_acl_connection), nullptr /* LinkManager* */);
   AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
   LOG_INFO("------------------");
@@ -72,7 +76,11 @@
 
 TEST_F(L2capLeFixedChannelImplTest, close_triggers_callback) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockLeAclConnection* mock_acl_connection = new testing::MockLeAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetRemoteAddress()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockLeAclConnection>(mock_acl_connection), nullptr /* LinkManager* */);
   AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
@@ -93,7 +101,11 @@
 
 TEST_F(L2capLeFixedChannelImplTest, register_callback_after_close_should_call_immediately) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockLeAclConnection* mock_acl_connection = new testing::MockLeAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetRemoteAddress()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockLeAclConnection>(mock_acl_connection), nullptr /* LinkManager* */);
   AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
@@ -114,7 +126,11 @@
 
 TEST_F(L2capLeFixedChannelImplTest, close_twice_should_fail) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockLeAclConnection* mock_acl_connection = new testing::MockLeAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetRemoteAddress()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockLeAclConnection>(mock_acl_connection), nullptr /* LinkManager* */);
   AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
@@ -138,7 +154,11 @@
 
 TEST_F(L2capLeFixedChannelImplTest, multiple_registeration_should_fail) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockLeAclConnection* mock_acl_connection = new testing::MockLeAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetRemoteAddress()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockLeAclConnection>(mock_acl_connection), nullptr /* LinkManager* */);
   AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
@@ -158,7 +178,11 @@
 
 TEST_F(L2capLeFixedChannelImplTest, call_acquire_before_registeration_should_fail) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockLeAclConnection* mock_acl_connection = new testing::MockLeAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetRemoteAddress()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockLeAclConnection>(mock_acl_connection), nullptr /* LinkManager* */);
   AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
@@ -167,7 +191,11 @@
 
 TEST_F(L2capLeFixedChannelImplTest, call_release_before_registeration_should_fail) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockLeAclConnection* mock_acl_connection = new testing::MockLeAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetRemoteAddress()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockLeAclConnection>(mock_acl_connection), nullptr /* LinkManager* */);
   AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
@@ -176,7 +204,11 @@
 
 TEST_F(L2capLeFixedChannelImplTest, test_acquire_release_channel) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockLeAclConnection* mock_acl_connection = new testing::MockLeAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetRemoteAddress()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockLeAclConnection>(mock_acl_connection), nullptr /* LinkManager* */);
   AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
@@ -204,7 +236,11 @@
 
 TEST_F(L2capLeFixedChannelImplTest, test_acquire_after_close) {
   MockParameterProvider mock_parameter_provider;
-  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider);
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockLeAclConnection* mock_acl_connection = new testing::MockLeAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetRemoteAddress()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockLeAclConnection>(mock_acl_connection), nullptr /* LinkManager* */);
   AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
   EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
   FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
diff --git a/gd/l2cap/le/internal/link.cc b/gd/l2cap/le/internal/link.cc
index 2f147a7..08abb97 100644
--- a/gd/l2cap/le/internal/link.cc
+++ b/gd/l2cap/le/internal/link.cc
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
+#include "l2cap/le/internal/link.h"
+
 #include <chrono>
 #include <memory>
 
-#include "hci/acl_manager.h"
+#include "hci/acl_manager/le_acl_connection.h"
 #include "l2cap/internal/dynamic_channel_impl.h"
 #include "l2cap/internal/parameter_provider.h"
 #include "l2cap/le/dynamic_channel_manager.h"
 #include "l2cap/le/internal/fixed_channel_impl.h"
-#include "l2cap/le/internal/link.h"
+#include "l2cap/le/internal/link_manager.h"
 #include "os/alarm.h"
 
 namespace bluetooth {
@@ -30,42 +32,84 @@
 namespace le {
 namespace internal {
 
-Link::Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
+static constexpr uint16_t kDefaultMinimumCeLength = 0x0002;
+static constexpr uint16_t kDefaultMaximumCeLength = 0x0C00;
+
+Link::Link(os::Handler* l2cap_handler, std::unique_ptr<hci::acl_manager::LeAclConnection> acl_connection,
            l2cap::internal::ParameterProvider* parameter_provider,
            DynamicChannelServiceManagerImpl* dynamic_service_manager,
-           FixedChannelServiceManagerImpl* fixed_service_manager)
+           FixedChannelServiceManagerImpl* fixed_service_manager, LinkManager* link_manager)
     : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)),
       data_pipeline_manager_(l2cap_handler, this, acl_connection_->GetAclQueueEnd()),
       parameter_provider_(parameter_provider), dynamic_service_manager_(dynamic_service_manager),
       signalling_manager_(l2cap_handler_, this, &data_pipeline_manager_, dynamic_service_manager_,
-                          &dynamic_channel_allocator_) {
+                          &dynamic_channel_allocator_),
+      link_manager_(link_manager) {
   ASSERT(l2cap_handler_ != nullptr);
   ASSERT(acl_connection_ != nullptr);
   ASSERT(parameter_provider_ != nullptr);
   link_idle_disconnect_alarm_.Schedule(common::BindOnce(&Link::Disconnect, common::Unretained(this)),
                                        parameter_provider_->GetLeLinkIdleDisconnectTimeout());
+  acl_connection_->RegisterCallbacks(this, l2cap_handler_);
 }
 
-void Link::OnAclDisconnected(hci::ErrorCode status) {
-  fixed_channel_allocator_.OnAclDisconnected(status);
-  dynamic_channel_allocator_.OnAclDisconnected(status);
+void Link::OnAclDisconnected(hci::ErrorCode reason) {
+  fixed_channel_allocator_.OnAclDisconnected(static_cast<hci::ErrorCode>(reason));
+  dynamic_channel_allocator_.OnAclDisconnected(static_cast<hci::ErrorCode>(reason));
+}
+
+void Link::OnDisconnection(hci::ErrorCode status) {
+  OnAclDisconnected(status);
+
+  link_manager_->OnDisconnect(GetAclConnection()->GetRemoteAddress());
+}
+
+void Link::OnConnectionUpdate(uint16_t connection_interval, uint16_t connection_latency, uint16_t supervision_timeout) {
+  LOG_DEBUG("interval %hx latency %hx supervision_timeout %hx", connection_interval, connection_latency,
+            supervision_timeout);
+  if (update_request_signal_id_ != kInvalidSignalId) {
+    hci::ErrorCode result = hci::ErrorCode::SUCCESS;
+    if (connection_interval > update_request_interval_max_ || connection_interval < update_request_interval_min_ ||
+        connection_latency != update_request_latency_ || supervision_timeout != update_request_supervision_timeout_) {
+      result = hci::ErrorCode::UNSPECIFIED_ERROR;
+    }
+    on_connection_update_complete(update_request_signal_id_, result);
+    update_request_signal_id_ = kInvalidSignalId;
+  }
 }
 
 void Link::Disconnect() {
   acl_connection_->Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
 }
 
-void Link::UpdateConnectionParameter(SignalId signal_id, uint16_t conn_interval_min, uint16_t conn_interval_max,
-                                     uint16_t conn_latency, uint16_t supervision_timeout) {
-  acl_connection_->LeConnectionUpdate(
-      conn_interval_min, conn_interval_max, conn_latency, supervision_timeout,
-      common::BindOnce(&Link::on_connection_update_complete, common::Unretained(this), signal_id), l2cap_handler_);
+void Link::UpdateConnectionParameterFromRemote(SignalId signal_id, uint16_t conn_interval_min,
+                                               uint16_t conn_interval_max, uint16_t conn_latency,
+                                               uint16_t supervision_timeout) {
+  acl_connection_->LeConnectionUpdate(conn_interval_min, conn_interval_max, conn_latency, supervision_timeout,
+                                      kDefaultMinimumCeLength, kDefaultMaximumCeLength);
+  update_request_signal_id_ = signal_id;
+  update_request_interval_min_ = conn_interval_min;
+  update_request_interval_max_ = conn_interval_max;
+  update_request_latency_ = conn_latency;
+  update_request_supervision_timeout_ = supervision_timeout;
+}
+
+void Link::SendConnectionParameterUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
+                                         uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length) {
+  if (acl_connection_->GetRole() == hci::Role::SLAVE) {
+    // TODO: If both LL master and slave support 4.1, use HCI command directly
+    signalling_manager_.SendConnectionParameterUpdateRequest(conn_interval_min, conn_interval_max, conn_latency,
+                                                             supervision_timeout);
+    return;
+  }
+  acl_connection_->LeConnectionUpdate(conn_interval_min, conn_interval_max, conn_latency, supervision_timeout,
+                                      min_ce_length, max_ce_length);
+  update_request_signal_id_ = kInvalidSignalId;
 }
 
 std::shared_ptr<FixedChannelImpl> Link::AllocateFixedChannel(Cid cid, SecurityPolicy security_policy) {
-  auto channel = fixed_channel_allocator_.AllocateChannel(cid, security_policy);
-  data_pipeline_manager_.AttachChannel(cid, channel,
-                                       l2cap::internal::DataPipelineManager::ChannelMode::LE_CREDIT_BASED);
+  auto channel = fixed_channel_allocator_.AllocateChannel(cid);
+  data_pipeline_manager_.AttachChannel(cid, channel, l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
   return channel;
 }
 
@@ -83,7 +127,9 @@
     return;
   }
   auto reserved_cid = ReserveDynamicChannel();
-  signalling_manager_.SendConnectionRequest(psm, reserved_cid, pending_dynamic_channel_connection.configuration_.mtu);
+  auto mtu = pending_dynamic_channel_connection.configuration_.mtu;
+  local_cid_to_pending_dynamic_channel_connection_map_[reserved_cid] = std::move(pending_dynamic_channel_connection);
+  signalling_manager_.SendConnectionRequest(psm, reserved_cid, mtu);
 }
 
 void Link::SendDisconnectionRequest(Cid local_cid, Cid remote_cid) {
@@ -94,14 +140,23 @@
   signalling_manager_.SendDisconnectRequest(local_cid, remote_cid);
 }
 
-void Link::OnOutgoingConnectionRequestFail(Cid local_cid) {
-  local_cid_to_pending_dynamic_channel_connection_map_.erase(local_cid);
+void Link::OnOutgoingConnectionRequestFail(Cid local_cid, LeCreditBasedConnectionResponseResult response_result) {
+  if (local_cid_to_pending_dynamic_channel_connection_map_.find(local_cid) !=
+      local_cid_to_pending_dynamic_channel_connection_map_.end()) {
+    // TODO(hsz): Currently we only notify the client when the remote didn't send connection response SUCCESS.
+    //  Should we notify the client when the link failed to establish?
+    DynamicChannelManager::ConnectionResult result{
+        .connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR,
+        .hci_error = hci::ErrorCode::SUCCESS,
+        .l2cap_connection_response_result = response_result,
+    };
+    NotifyChannelFail(local_cid, result);
+  }
   dynamic_channel_allocator_.FreeChannel(local_cid);
 }
 
-std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateDynamicChannel(Psm psm, Cid remote_cid,
-                                                                                  SecurityPolicy security_policy) {
-  auto channel = dynamic_channel_allocator_.AllocateChannel(psm, remote_cid, security_policy);
+std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateDynamicChannel(Psm psm, Cid remote_cid) {
+  auto channel = dynamic_channel_allocator_.AllocateChannel(psm, remote_cid);
   if (channel != nullptr) {
     data_pipeline_manager_.AttachChannel(channel->GetCid(), channel,
                                          l2cap::internal::DataPipelineManager::ChannelMode::LE_CREDIT_BASED);
@@ -111,9 +166,9 @@
   return channel;
 }
 
-std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateReservedDynamicChannel(
-    Cid reserved_cid, Psm psm, Cid remote_cid, SecurityPolicy security_policy) {
-  auto channel = dynamic_channel_allocator_.AllocateReservedChannel(reserved_cid, psm, remote_cid, security_policy);
+std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateReservedDynamicChannel(Cid reserved_cid, Psm psm,
+                                                                                          Cid remote_cid) {
+  auto channel = dynamic_channel_allocator_.AllocateReservedChannel(reserved_cid, psm, remote_cid);
   if (channel != nullptr) {
     data_pipeline_manager_.AttachChannel(channel->GetCid(), channel,
                                          l2cap::internal::DataPipelineManager::ChannelMode::LE_CREDIT_BASED);
@@ -123,12 +178,6 @@
   return channel;
 }
 
-DynamicChannelConfigurationOption Link::GetConfigurationForInitialConfiguration(Cid cid) {
-  ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
-         local_cid_to_pending_dynamic_channel_connection_map_.end());
-  return local_cid_to_pending_dynamic_channel_connection_map_[cid].configuration_;
-}
-
 void Link::FreeDynamicChannel(Cid cid) {
   if (dynamic_channel_allocator_.FindChannelByCid(cid) == nullptr) {
     return;
@@ -160,12 +209,11 @@
   local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
 }
 
-void Link::NotifyChannelFail(Cid cid) {
+void Link::NotifyChannelFail(Cid cid, DynamicChannelManager::ConnectionResult result) {
   ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
          local_cid_to_pending_dynamic_channel_connection_map_.end());
   auto& pending_dynamic_channel_connection = local_cid_to_pending_dynamic_channel_connection_map_[cid];
   // TODO(cmanton) Pass proper connection falure result to user
-  DynamicChannelManager::ConnectionResult result;
   pending_dynamic_channel_connection.handler_->Post(
       common::BindOnce(std::move(pending_dynamic_channel_connection.on_fail_callback_), result));
   local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
@@ -184,6 +232,10 @@
 }
 
 void Link::on_connection_update_complete(SignalId signal_id, hci::ErrorCode error_code) {
+  if (!signal_id.IsValid()) {
+    LOG_INFO("Invalid signal_id");
+    return;
+  }
   ConnectionParameterUpdateResponseResult result = (error_code == hci::ErrorCode::SUCCESS)
                                                        ? ConnectionParameterUpdateResponseResult::ACCEPTED
                                                        : ConnectionParameterUpdateResponseResult::REJECTED;
diff --git a/gd/l2cap/le/internal/link.h b/gd/l2cap/le/internal/link.h
index 1d4ed3a..355dda8 100644
--- a/gd/l2cap/le/internal/link.h
+++ b/gd/l2cap/le/internal/link.h
@@ -19,7 +19,7 @@
 #include <chrono>
 #include <memory>
 
-#include "hci/acl_manager.h"
+#include "hci/acl_manager/le_acl_connection.h"
 #include "l2cap/internal/data_pipeline_manager.h"
 #include "l2cap/internal/dynamic_channel_allocator.h"
 #include "l2cap/internal/fixed_channel_allocator.h"
@@ -30,6 +30,8 @@
 #include "l2cap/le/internal/fixed_channel_impl.h"
 #include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
 #include "l2cap/le/internal/signalling_manager.h"
+#include "l2cap/le/link_options.h"
+#include "l2cap/le/security_enforcement_interface.h"
 #include "os/alarm.h"
 
 namespace bluetooth {
@@ -37,17 +39,19 @@
 namespace le {
 namespace internal {
 
-class Link : public l2cap::internal::ILink {
+class LinkManager;
+
+class Link : public l2cap::internal::ILink, public hci::acl_manager::LeConnectionManagementCallbacks {
  public:
-  Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
+  Link(os::Handler* l2cap_handler, std::unique_ptr<hci::acl_manager::LeAclConnection> acl_connection,
        l2cap::internal::ParameterProvider* parameter_provider,
-       DynamicChannelServiceManagerImpl* dynamic_service_manager,
-       FixedChannelServiceManagerImpl* fixed_service_manager);
+       DynamicChannelServiceManagerImpl* dynamic_service_manager, FixedChannelServiceManagerImpl* fixed_service_manager,
+       LinkManager* link_manager);
 
   ~Link() override = default;
 
   inline hci::AddressWithType GetDevice() override {
-    return {acl_connection_->GetAddress(), acl_connection_->GetAddressType()};
+    return acl_connection_->GetRemoteAddress();
   }
 
   struct PendingDynamicChannelConnection {
@@ -61,15 +65,29 @@
     return acl_connection_->GetRole();
   }
 
+  inline virtual hci::acl_manager::LeAclConnection* GetAclConnection() {
+    return acl_connection_.get();
+  }
+
   // ACL methods
 
-  virtual void OnAclDisconnected(hci::ErrorCode status);
+  virtual void OnAclDisconnected(hci::ErrorCode reason);
+
+  void OnDisconnection(hci::ErrorCode reason) override;
+
+  void OnConnectionUpdate(uint16_t connection_interval, uint16_t connection_latency,
+                          uint16_t supervision_timeout) override;
 
   virtual void Disconnect();
 
   // Handles connection parameter update request from remote
-  virtual void UpdateConnectionParameter(SignalId signal_id, uint16_t conn_interval_min, uint16_t conn_interval_max,
-                                         uint16_t conn_latency, uint16_t supervision_timeout);
+  virtual void UpdateConnectionParameterFromRemote(SignalId signal_id, uint16_t conn_interval_min,
+                                                   uint16_t conn_interval_max, uint16_t conn_latency,
+                                                   uint16_t supervision_timeout);
+
+  virtual void SendConnectionParameterUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max,
+                                             uint16_t conn_latency, uint16_t supervision_timeout,
+                                             uint16_t min_ce_length, uint16_t max_ce_length);
 
   // FixedChannel methods
 
@@ -86,15 +104,12 @@
   void SendDisconnectionRequest(Cid local_cid, Cid remote_cid) override;
 
   // Invoked by signalling manager to indicate an outgoing connection request failed and link shall free resources
-  virtual void OnOutgoingConnectionRequestFail(Cid local_cid);
+  virtual void OnOutgoingConnectionRequestFail(Cid local_cid, LeCreditBasedConnectionResponseResult result);
 
-  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateDynamicChannel(Psm psm, Cid remote_cid,
-                                                                                      SecurityPolicy security_policy);
+  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateDynamicChannel(Psm psm, Cid remote_cid);
 
-  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateReservedDynamicChannel(
-      Cid reserved_cid, Psm psm, Cid remote_cid, SecurityPolicy security_policy);
-
-  virtual DynamicChannelConfigurationOption GetConfigurationForInitialConfiguration(Cid cid);
+  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateReservedDynamicChannel(Cid reserved_cid, Psm psm,
+                                                                                              Cid remote_cid);
 
   virtual void FreeDynamicChannel(Cid cid);
 
@@ -102,7 +117,7 @@
   virtual void RefreshRefCount();
 
   void NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> user_channel);
-  void NotifyChannelFail(Cid cid);
+  void NotifyChannelFail(Cid cid, DynamicChannelManager::ConnectionResult result);
 
   virtual std::string ToString() {
     return GetDevice().ToString();
@@ -114,19 +129,33 @@
 
   void SendLeCredit(Cid local_cid, uint16_t credit) override;
 
+  LinkOptions* GetLinkOptions() {
+    return &link_options_;
+  }
+
  private:
   os::Handler* l2cap_handler_;
   l2cap::internal::FixedChannelAllocator<FixedChannelImpl, Link> fixed_channel_allocator_{this, l2cap_handler_};
   l2cap::internal::DynamicChannelAllocator dynamic_channel_allocator_{this, l2cap_handler_};
-  std::unique_ptr<hci::AclConnection> acl_connection_;
+  std::unique_ptr<hci::acl_manager::LeAclConnection> acl_connection_;
   l2cap::internal::DataPipelineManager data_pipeline_manager_;
   l2cap::internal::ParameterProvider* parameter_provider_;
   DynamicChannelServiceManagerImpl* dynamic_service_manager_;
   LeSignallingManager signalling_manager_;
   std::unordered_map<Cid, PendingDynamicChannelConnection> local_cid_to_pending_dynamic_channel_connection_map_;
   os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
+  LinkOptions link_options_{acl_connection_.get(), this, l2cap_handler_};
+  LinkManager* link_manager_;
+  SignalId update_request_signal_id_ = kInvalidSignalId;
+  uint16_t update_request_interval_min_;
+  uint16_t update_request_interval_max_;
+  uint16_t update_request_latency_;
+  uint16_t update_request_supervision_timeout_;
   DISALLOW_COPY_AND_ASSIGN(Link);
 
+  // Received connection update complete from ACL manager. SignalId is bound to a valid number when we need to send a
+  // response to remote. If SignalId is bound to an invalid number, we don't send a response to remote, because the
+  // connection update request is not from remote LL slave.
   void on_connection_update_complete(SignalId signal_id, hci::ErrorCode error_code);
 };
 
diff --git a/gd/l2cap/le/internal/link_manager.cc b/gd/l2cap/le/internal/link_manager.cc
index 35a3ff2..e2edcbe 100644
--- a/gd/l2cap/le/internal/link_manager.cc
+++ b/gd/l2cap/le/internal/link_manager.cc
@@ -16,7 +16,7 @@
 #include <memory>
 #include <unordered_map>
 
-#include "hci/acl_manager.h"
+#include "hci/acl_manager/le_acl_connection.h"
 #include "hci/address.h"
 #include "l2cap/internal/scheduler_fifo.h"
 #include "l2cap/le/internal/link.h"
@@ -54,7 +54,8 @@
         continue;
       }
       // Allocate channel for newly registered fixed channels
-      auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+      auto fixed_channel_impl = link->AllocateFixedChannel(
+          fixed_channel_service.first, SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK);
       fixed_channel_service.second->NotifyChannelCreation(
           std::make_unique<FixedChannel>(fixed_channel_impl, l2cap_handler_));
       num_new_channels++;
@@ -101,22 +102,19 @@
 }
 
 void LinkManager::OnLeConnectSuccess(hci::AddressWithType connecting_address_with_type,
-                                     std::unique_ptr<hci::AclConnection> acl_connection) {
+                                     std::unique_ptr<hci::acl_manager::LeAclConnection> acl_connection) {
   // Same link should not be connected twice
-  hci::AddressWithType connected_address_with_type(acl_connection->GetAddress(), acl_connection->GetAddressType());
+  hci::AddressWithType connected_address_with_type = acl_connection->GetRemoteAddress();
   ASSERT_LOG(GetLink(connected_address_with_type) == nullptr, "%s is connected twice without disconnection",
-             acl_connection->GetAddress().ToString().c_str());
-  // Register ACL disconnection callback in LinkManager so that we can clean up link resource properly
-  acl_connection->RegisterDisconnectCallback(
-      common::BindOnce(&LinkManager::OnDisconnect, common::Unretained(this), connected_address_with_type),
-      l2cap_handler_);
+             acl_connection->GetRemoteAddress().ToString().c_str());
   links_.try_emplace(connected_address_with_type, l2cap_handler_, std::move(acl_connection), parameter_provider_,
-                     dynamic_channel_service_manager_, fixed_channel_service_manager_);
+                     dynamic_channel_service_manager_, fixed_channel_service_manager_, this);
   auto* link = GetLink(connected_address_with_type);
   // Allocate and distribute channels for all registered fixed channel services
   auto fixed_channel_services = fixed_channel_service_manager_->GetRegisteredServices();
   for (auto& fixed_channel_service : fixed_channel_services) {
-    auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+    auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first,
+                                                         SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK);
     fixed_channel_service.second->NotifyChannelCreation(
         std::make_unique<FixedChannel>(fixed_channel_impl, l2cap_handler_));
   }
@@ -128,13 +126,7 @@
   }
 
   // Remove device from pending links list, if any
-  auto pending_link = pending_links_.find(connecting_address_with_type);
-  if (pending_link == pending_links_.end()) {
-    // This an incoming connection, exit
-    return;
-  }
-  // This is an outgoing connection, remove entry in pending link list
-  pending_links_.erase(pending_link);
+  pending_links_.erase(connecting_address_with_type);
 }
 
 void LinkManager::OnLeConnectFail(hci::AddressWithType address_with_type, hci::ErrorCode reason) {
@@ -155,11 +147,10 @@
   pending_links_.erase(pending_link);
 }
 
-void LinkManager::OnDisconnect(hci::AddressWithType address_with_type, hci::ErrorCode status) {
+void LinkManager::OnDisconnect(bluetooth::hci::AddressWithType address_with_type) {
   auto* link = GetLink(address_with_type);
-  ASSERT_LOG(link != nullptr, "Device %s is disconnected with reason 0x%x, but not in local database",
-             address_with_type.ToString().c_str(), static_cast<uint8_t>(status));
-  link->OnAclDisconnected(status);
+  ASSERT_LOG(link != nullptr, "Device %s is disconnected but not in local database",
+             address_with_type.ToString().c_str());
   links_.erase(address_with_type);
 }
 
diff --git a/gd/l2cap/le/internal/link_manager.h b/gd/l2cap/le/internal/link_manager.h
index 9e9c0e8..0bcca94 100644
--- a/gd/l2cap/le/internal/link_manager.h
+++ b/gd/l2cap/le/internal/link_manager.h
@@ -22,12 +22,13 @@
 
 #include "os/handler.h"
 
-#include "hci/acl_manager.h"
+#include "hci/acl_manager/le_acl_connection.h"
 #include "hci/address.h"
 #include "hci/address_with_type.h"
 #include "l2cap/internal/parameter_provider.h"
 #include "l2cap/internal/scheduler.h"
 #include "l2cap/le/fixed_channel_manager.h"
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
 #include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
 #include "l2cap/le/internal/link.h"
 
@@ -36,12 +37,13 @@
 namespace le {
 namespace internal {
 
-class LinkManager : public hci::LeConnectionCallbacks {
+class LinkManager : public hci::acl_manager::LeConnectionCallbacks {
  public:
   LinkManager(os::Handler* l2cap_handler, hci::AclManager* acl_manager, FixedChannelServiceManagerImpl* service_manager,
+              DynamicChannelServiceManagerImpl* dynamic_service_manager,
               l2cap::internal::ParameterProvider* parameter_provider)
       : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager), fixed_channel_service_manager_(service_manager),
-        parameter_provider_(parameter_provider) {
+        dynamic_channel_service_manager_(dynamic_service_manager), parameter_provider_(parameter_provider) {
     acl_manager_->RegisterLeCallbacks(this, l2cap_handler_);
   }
 
@@ -58,9 +60,8 @@
 
   Link* GetLink(hci::AddressWithType address_with_type);
   void OnLeConnectSuccess(hci::AddressWithType connecting_address_with_type,
-                          std::unique_ptr<hci::AclConnection> acl_connection) override;
+                          std::unique_ptr<hci::acl_manager::LeAclConnection> acl_connection) override;
   void OnLeConnectFail(hci::AddressWithType address_with_type, hci::ErrorCode reason) override;
-  void OnDisconnect(hci::AddressWithType address_with_type, hci::ErrorCode status);
 
   // FixedChannelManager methods
 
@@ -72,6 +73,8 @@
   void ConnectDynamicChannelServices(hci::AddressWithType device,
                                      Link::PendingDynamicChannelConnection pending_dynamic_channel_connection, Psm psm);
 
+  void OnDisconnect(hci::AddressWithType address_with_type);
+
  private:
   // Dependencies
   os::Handler* l2cap_handler_;
diff --git a/gd/l2cap/le/internal/link_manager_test.cc b/gd/l2cap/le/internal/link_manager_test.cc
index 59d8d6c..dd53aab 100644
--- a/gd/l2cap/le/internal/link_manager_test.cc
+++ b/gd/l2cap/le/internal/link_manager_test.cc
@@ -38,9 +38,10 @@
 namespace l2cap {
 namespace le {
 namespace internal {
+namespace {
 
-using hci::testing::MockAclConnection;
 using hci::testing::MockAclManager;
+using hci::testing::MockLeAclConnection;
 using l2cap::internal::testing::MockParameterProvider;
 using ::testing::_;  // Matcher to any value
 using ::testing::ByMove;
@@ -51,7 +52,7 @@
 using ::testing::SaveArg;
 
 constexpr static auto kTestIdleDisconnectTimeoutLong = std::chrono::milliseconds(1000);
-constexpr static auto kTestIdleDisconnectTimeoutShort = std::chrono::milliseconds(30);
+constexpr static auto kTestIdleDisconnectTimeoutShort = std::chrono::milliseconds(1000);
 
 class L2capLeLinkManagerTest : public ::testing::Test {
  public:
@@ -66,6 +67,7 @@
   void SetUp() override {
     thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
     l2cap_handler_ = new os::Handler(thread_);
+    user_handler_ = new os::Handler(thread_);
     mock_parameter_provider_ = new MockParameterProvider;
     EXPECT_CALL(*mock_parameter_provider_, GetLeLinkIdleDisconnectTimeout)
         .WillRepeatedly(Return(kTestIdleDisconnectTimeoutLong));
@@ -75,11 +77,14 @@
     delete mock_parameter_provider_;
     l2cap_handler_->Clear();
     delete l2cap_handler_;
+    user_handler_->Clear();
+    delete user_handler_;
     delete thread_;
   }
 
   os::Thread* thread_ = nullptr;
   os::Handler* l2cap_handler_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
   MockParameterProvider* mock_parameter_provider_ = nullptr;
 };
 
@@ -88,14 +93,13 @@
   MockAclManager mock_acl_manager;
   hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                                          hci::AddressType::RANDOM_DEVICE_ADDRESS);
-  auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  hci::acl_manager::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
-  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
                               mock_parameter_provider_);
   EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
   EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -110,38 +114,50 @@
   // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
   EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
   LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
-      .handler_ = user_handler.get(),
+      .handler_ = user_handler_,
       .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
   le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
 
   // Step 3: ACL connection success event should trigger channel creation for all registered services
-
-  std::unique_ptr<MockAclConnection> acl_connection = std::make_unique<MockAclConnection>();
-  EXPECT_CALL(*acl_connection, RegisterDisconnectCallback(_, l2cap_handler_)).Times(1);
-  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(address_with_type.GetAddress()));
-  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(address_with_type.GetAddressType()));
+  std::unique_ptr<MockLeAclConnection> acl_connection = std::make_unique<MockLeAclConnection>();
+  EXPECT_CALL(*acl_connection, GetRemoteAddress()).WillRepeatedly(Return(address_with_type));
+  hci::acl_manager::LeConnectionManagementCallbacks* connection_management_callbacks = nullptr;
+  os::Handler* connection_management_handler = nullptr;
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&connection_management_callbacks), SaveArg<1>(&connection_management_handler)));
   std::unique_ptr<FixedChannel> channel_1, channel_2;
-  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).WillOnce([&channel_1](std::unique_ptr<FixedChannel> channel) {
-    channel_1 = std::move(channel);
-  });
-  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_)).WillOnce([&channel_2](std::unique_ptr<FixedChannel> channel) {
-    channel_2 = std::move(channel);
-  });
-  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectSuccess,
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::LeConnectionCallbacks::OnLeConnectSuccess,
                                               common::Unretained(hci_le_connection_callbacks), address_with_type,
                                               std::move(acl_connection)));
   SyncHandler(hci_callback_handler);
-  EXPECT_NE(channel_1, nullptr);
-  EXPECT_NE(channel_2, nullptr);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
+  ASSERT_NE(channel_1, nullptr);
+  ASSERT_NE(channel_2, nullptr);
 
-  // Step 4: Calling ConnectServices() to the same device will no trigger another connection attempt
+  // Step 4: Calling ConnectServices() to the same device will not trigger another connection attempt
   FixedChannelManager::ConnectionResult my_result;
   LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection_2{
-      .handler_ = user_handler.get(),
+      .handler_ = user_handler_,
       .on_fail_callback_ = common::testing::BindLambdaForTesting(
           [&my_result](FixedChannelManager::ConnectionResult result) { my_result = result; })};
   le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection_2));
-  SyncHandler(user_handler.get());
+  SyncHandler(user_handler_);
   EXPECT_EQ(my_result.connection_result_code,
             FixedChannelManager::ConnectionResultCode::FAIL_ALL_SERVICES_HAVE_CHANNEL);
 
@@ -150,18 +166,25 @@
   results.emplace_back(kSmpBrCid + 1, &mock_service_3);
   EXPECT_CALL(mock_le_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
   LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection_3{
-      .handler_ = user_handler.get(),
+      .handler_ = user_handler_,
       .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
   std::unique_ptr<FixedChannel> channel_3;
-  EXPECT_CALL(mock_service_3, NotifyChannelCreation(_)).WillOnce([&channel_3](std::unique_ptr<FixedChannel> channel) {
-    channel_3 = std::move(channel);
-  });
+  std::promise<void> promise_3;
+  auto future_3 = promise_3.get_future();
+  EXPECT_CALL(mock_service_3, NotifyChannelCreation(_))
+      .WillOnce([&channel_3, &promise_3](std::unique_ptr<FixedChannel> channel) {
+        channel_3 = std::move(channel);
+        promise_3.set_value();
+      });
   le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection_3));
+  auto future_3_status = future_3.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_3_status, std::future_status::ready);
   EXPECT_NE(channel_3, nullptr);
 
-  user_handler->Clear();
-
-  le_link_manager.OnDisconnect(address_with_type, hci::ErrorCode::SUCCESS);
+  connection_management_handler->Post(common::BindOnce(
+      &hci::acl_manager::LeConnectionManagementCallbacks::OnDisconnection,
+      common::Unretained(connection_management_callbacks), hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION));
+  SyncHandler(connection_management_handler);
 }
 
 TEST_F(L2capLeLinkManagerTest, connect_fixed_channel_service_without_acl_with_no_service) {
@@ -169,14 +192,13 @@
   MockAclManager mock_acl_manager;
   hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                                          hci::AddressType::PUBLIC_DEVICE_ADDRESS);
-  auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  hci::acl_manager::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
-  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
                               mock_parameter_provider_);
   EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
   EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -189,14 +211,13 @@
   EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(0);
   FixedChannelManager::ConnectionResult my_result;
   LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
-      .handler_ = user_handler.get(),
+      .handler_ = user_handler_,
       .on_fail_callback_ = common::testing::BindLambdaForTesting(
           [&my_result](FixedChannelManager::ConnectionResult result) { my_result = result; })};
   le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
-  SyncHandler(user_handler.get());
+  SyncHandler(user_handler_);
   EXPECT_EQ(my_result.connection_result_code, FixedChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED);
 
-  user_handler->Clear();
 }
 
 TEST_F(L2capLeLinkManagerTest, connect_fixed_channel_service_without_acl_with_hci_failure) {
@@ -204,14 +225,13 @@
   MockAclManager mock_acl_manager;
   hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                                          hci::AddressType::RANDOM_DEVICE_ADDRESS);
-  auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  hci::acl_manager::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
-  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
                               mock_parameter_provider_);
   EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
   EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -226,22 +246,20 @@
   EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
   FixedChannelManager::ConnectionResult my_result;
   LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
-      .handler_ = user_handler.get(),
+      .handler_ = user_handler_,
       .on_fail_callback_ = common::testing::BindLambdaForTesting(
           [&my_result](FixedChannelManager::ConnectionResult result) { my_result = result; })};
   le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
 
   // Step 3: ACL connection failure event should trigger connection failure callback
   EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).Times(0);
-  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectFail,
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::LeConnectionCallbacks::OnLeConnectFail,
                                               common::Unretained(hci_le_connection_callbacks), address_with_type,
                                               hci::ErrorCode::PAGE_TIMEOUT));
   SyncHandler(hci_callback_handler);
-  SyncHandler(user_handler.get());
+  SyncHandler(user_handler_);
   EXPECT_EQ(my_result.connection_result_code, FixedChannelManager::ConnectionResultCode::FAIL_HCI_ERROR);
   EXPECT_EQ(my_result.hci_error, hci::ErrorCode::PAGE_TIMEOUT);
-
-  user_handler->Clear();
 }
 
 TEST_F(L2capLeLinkManagerTest, not_acquiring_channels_should_disconnect_acl_after_timeout) {
@@ -251,14 +269,13 @@
   MockAclManager mock_acl_manager;
   hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                                          hci::AddressType::RANDOM_DEVICE_ADDRESS);
-  auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  hci::acl_manager::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
-  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
                               mock_parameter_provider_);
   EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
   EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -273,46 +290,61 @@
   // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
   EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
   LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
-      .handler_ = user_handler.get(),
+      .handler_ = user_handler_,
       .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
   le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
 
   // Step 3: ACL connection success event should trigger channel creation for all registered services
-  auto* raw_acl_connection = new MockAclConnection();
-  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
-  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(address_with_type.GetAddress()));
-  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(address_with_type.GetAddressType()));
+  auto* raw_acl_connection = new MockLeAclConnection();
+  std::unique_ptr<MockLeAclConnection> acl_connection(raw_acl_connection);
+  EXPECT_CALL(*acl_connection, GetRemoteAddress()).WillRepeatedly(Return(address_with_type));
+  hci::acl_manager::LeConnectionManagementCallbacks* connection_management_callbacks = nullptr;
+  os::Handler* connection_management_handler = nullptr;
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&connection_management_callbacks), SaveArg<1>(&connection_management_handler)));
   std::unique_ptr<FixedChannel> channel_1, channel_2;
-  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).WillOnce([&channel_1](std::unique_ptr<FixedChannel> channel) {
-    channel_1 = std::move(channel);
-  });
-  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_)).WillOnce([&channel_2](std::unique_ptr<FixedChannel> channel) {
-    channel_2 = std::move(channel);
-  });
-  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectSuccess,
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::LeConnectionCallbacks::OnLeConnectSuccess,
                                               common::Unretained(hci_le_connection_callbacks), address_with_type,
                                               std::move(acl_connection)));
   SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
   EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
   EXPECT_NE(channel_2, nullptr);
   hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
   channel_1->RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+      user_handler_, common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
   hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
   channel_2->RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+      user_handler_, common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
 
-  // Step 4: ave channel IDLE long enough, they will disconnect
+  // Step 4: Leave channel IDLE long enough, they will disconnect
   EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(1);
   std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
+  connection_management_handler->Post(common::BindOnce(
+      &hci::acl_manager::LeConnectionManagementCallbacks::OnDisconnection,
+      common::Unretained(connection_management_callbacks), hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST));
+  SyncHandler(connection_management_handler);
 
   // Step 5: Link disconnect will trigger all callbacks
-  le_link_manager.OnDisconnect(address_with_type, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
-  SyncHandler(user_handler.get());
+  SyncHandler(user_handler_);
   EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
   EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
-
-  user_handler->Clear();
 }
 
 TEST_F(L2capLeLinkManagerTest, acquiring_channels_should_not_disconnect_acl_after_timeout) {
@@ -322,14 +354,13 @@
   MockAclManager mock_acl_manager;
   hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                                          hci::AddressType::RANDOM_DEVICE_ADDRESS);
-  auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  hci::acl_manager::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
-  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
                               mock_parameter_provider_);
   EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
   EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -344,34 +375,48 @@
   // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
   EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
   LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
-      .handler_ = user_handler.get(),
+      .handler_ = user_handler_,
       .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
   le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
 
   // Step 3: ACL connection success event should trigger channel creation for all registered services
-  auto* raw_acl_connection = new MockAclConnection();
-  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
-  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(address_with_type.GetAddress()));
-  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(address_with_type.GetAddressType()));
+  auto* raw_acl_connection = new MockLeAclConnection();
+  std::unique_ptr<MockLeAclConnection> acl_connection(raw_acl_connection);
+  EXPECT_CALL(*acl_connection, GetRemoteAddress()).WillRepeatedly(Return(address_with_type));
+  hci::acl_manager::LeConnectionManagementCallbacks* connection_management_callbacks = nullptr;
+  os::Handler* connection_management_handler = nullptr;
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&connection_management_callbacks), SaveArg<1>(&connection_management_handler)));
   std::unique_ptr<FixedChannel> channel_1, channel_2;
-  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).WillOnce([&channel_1](std::unique_ptr<FixedChannel> channel) {
-    channel_1 = std::move(channel);
-  });
-  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_)).WillOnce([&channel_2](std::unique_ptr<FixedChannel> channel) {
-    channel_2 = std::move(channel);
-  });
-  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectSuccess,
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::LeConnectionCallbacks::OnLeConnectSuccess,
                                               common::Unretained(hci_le_connection_callbacks), address_with_type,
                                               std::move(acl_connection)));
   SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
   EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
   EXPECT_NE(channel_2, nullptr);
   hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
   channel_1->RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+      user_handler_, common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
   hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
   channel_2->RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+      user_handler_, common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
 
   channel_1->Acquire();
 
@@ -380,12 +425,13 @@
   std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 2);
 
   // Step 5: Link disconnect will trigger all callbacks
-  le_link_manager.OnDisconnect(address_with_type, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
-  SyncHandler(user_handler.get());
+  connection_management_handler->Post(common::BindOnce(
+      &hci::acl_manager::LeConnectionManagementCallbacks::OnDisconnection,
+      common::Unretained(connection_management_callbacks), hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST));
+  SyncHandler(connection_management_handler);
+  SyncHandler(user_handler_);
   EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
   EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
-
-  user_handler->Clear();
 }
 
 TEST_F(L2capLeLinkManagerTest, acquiring_and_releasing_channels_should_eventually_disconnect_acl) {
@@ -395,14 +441,13 @@
   MockAclManager mock_acl_manager;
   hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
                                          hci::AddressType::PUBLIC_IDENTITY_ADDRESS);
-  auto user_handler = std::make_unique<os::Handler>(thread_);
 
   // Step 1: Verify callback registration with HCI
-  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  hci::acl_manager::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
   os::Handler* hci_callback_handler = nullptr;
   EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
       .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
-  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager, nullptr,
                               mock_parameter_provider_);
   EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
   EXPECT_EQ(hci_callback_handler, l2cap_handler_);
@@ -417,34 +462,48 @@
   // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
   EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
   LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
-      .handler_ = user_handler.get(),
+      .handler_ = user_handler_,
       .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
   le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
 
   // Step 3: ACL connection success event should trigger channel creation for all registered services
-  auto* raw_acl_connection = new MockAclConnection();
-  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
-  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(address_with_type.GetAddress()));
-  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(address_with_type.GetAddressType()));
+  auto* raw_acl_connection = new MockLeAclConnection();
+  std::unique_ptr<MockLeAclConnection> acl_connection(raw_acl_connection);
+  EXPECT_CALL(*acl_connection, GetRemoteAddress()).WillRepeatedly(Return(address_with_type));
+  hci::acl_manager::LeConnectionManagementCallbacks* connection_management_callbacks = nullptr;
+  os::Handler* connection_management_handler = nullptr;
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&connection_management_callbacks), SaveArg<1>(&connection_management_handler)));
   std::unique_ptr<FixedChannel> channel_1, channel_2;
-  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).WillOnce([&channel_1](std::unique_ptr<FixedChannel> channel) {
-    channel_1 = std::move(channel);
-  });
-  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_)).WillOnce([&channel_2](std::unique_ptr<FixedChannel> channel) {
-    channel_2 = std::move(channel);
-  });
-  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectSuccess,
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::acl_manager::LeConnectionCallbacks::OnLeConnectSuccess,
                                               common::Unretained(hci_le_connection_callbacks), address_with_type,
                                               std::move(acl_connection)));
   SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
   EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
   EXPECT_NE(channel_2, nullptr);
   hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
   channel_1->RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+      user_handler_, common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
   hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
   channel_2->RegisterOnCloseCallback(
-      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+      user_handler_, common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
 
   channel_1->Acquire();
 
@@ -458,14 +517,16 @@
   std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
 
   // Step 6: Link disconnect will trigger all callbacks
-  le_link_manager.OnDisconnect(address_with_type, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
-  SyncHandler(user_handler.get());
+  connection_management_handler->Post(common::BindOnce(
+      &hci::acl_manager::LeConnectionManagementCallbacks::OnDisconnection,
+      common::Unretained(connection_management_callbacks), hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST));
+  SyncHandler(connection_management_handler);
+  SyncHandler(user_handler_);
   EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
   EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
-
-  user_handler->Clear();
 }
 
+}  // namespace
 }  // namespace internal
 }  // namespace le
 }  // namespace l2cap
diff --git a/gd/l2cap/le/internal/link_mock.h b/gd/l2cap/le/internal/link_mock.h
index cb2292c..e9ce98b 100644
--- a/gd/l2cap/le/internal/link_mock.h
+++ b/gd/l2cap/le/internal/link_mock.h
@@ -29,12 +29,14 @@
 namespace internal {
 namespace testing {
 
-using hci::testing::MockAclConnection;
+using hci::testing::MockLeAclConnection;
 
 class MockLink : public Link {
  public:
-  explicit MockLink(os::Handler* handler, l2cap::internal::ParameterProvider* parameter_provider)
-      : Link(handler, std::make_unique<MockAclConnection>(), parameter_provider, nullptr, nullptr){};
+  explicit MockLink(os::Handler* handler, l2cap::internal::ParameterProvider* parameter_provider,
+                    std::unique_ptr<MockLeAclConnection> mock_acl_connection, LinkManager* link_manager)
+      : Link(handler, std::move(mock_acl_connection), parameter_provider, nullptr, nullptr, link_manager) {}
+
   MOCK_METHOD(hci::AddressWithType, GetDevice, (), (override));
   MOCK_METHOD(hci::Role, GetRole, (), (override));
   MOCK_METHOD(void, OnAclDisconnected, (hci::ErrorCode status), (override));
diff --git a/gd/l2cap/le/internal/signalling_manager.cc b/gd/l2cap/le/internal/signalling_manager.cc
index 2922a74..26076e1 100644
--- a/gd/l2cap/le/internal/signalling_manager.cc
+++ b/gd/l2cap/le/internal/signalling_manager.cc
@@ -42,7 +42,8 @@
       dynamic_service_manager_(dynamic_service_manager), channel_allocator_(channel_allocator), alarm_(handler) {
   ASSERT(handler_ != nullptr);
   ASSERT(link_ != nullptr);
-  signalling_channel_ = link_->AllocateFixedChannel(kClassicSignallingCid, {});
+  signalling_channel_ =
+      link_->AllocateFixedChannel(kLeSignallingCid, SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK);
   signalling_channel_->GetQueueUpEnd()->RegisterDequeue(
       handler_, common::Bind(&LeSignallingManager::on_incoming_packet, common::Unretained(this)));
   enqueue_buffer_ =
@@ -56,9 +57,20 @@
 }
 
 void LeSignallingManager::SendConnectionRequest(Psm psm, Cid local_cid, Mtu mtu) {
-  PendingCommand pending_command = {
-      next_signal_id_, LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST, psm, local_cid, {}, mtu, link_->GetMps(),
-      link_->GetInitialCredit()};
+  dynamic_service_manager_->GetSecurityEnforcementInterface()->Enforce(
+      link_->GetDevice(),
+      dynamic_service_manager_->GetService(psm)->GetSecurityPolicy(),
+      handler_->BindOnceOn(this, &LeSignallingManager::on_security_result_for_outgoing, psm, local_cid, mtu));
+}
+
+void LeSignallingManager::on_security_result_for_outgoing(Psm psm, Cid local_cid, Mtu mtu, bool result) {
+  if (!result) {
+    LOG_WARN("Security requirement can't be satisfied. Dropping connection request");
+    return;
+  }
+
+  PendingCommand pending_command = PendingCommand::CreditBasedConnectionRequest(
+      next_signal_id_, psm, local_cid, mtu, link_->GetMps(), link_->GetInitialCredit());
   next_signal_id_++;
   pending_commands_.push(pending_command);
   if (pending_commands_.size() == 1) {
@@ -66,9 +78,8 @@
   }
 }
 
-void LeSignallingManager::SendDisconnectRequest(Cid local_cid, Cid remote_cid) {
-  PendingCommand pending_command = {
-      next_signal_id_, LeCommandCode::DISCONNECTION_REQUEST, {}, local_cid, remote_cid, {}, {}, {}};
+void LeSignallingManager::SendDisconnectRequest(Cid scid, Cid dcid) {
+  PendingCommand pending_command = PendingCommand::DisconnectionRequest(next_signal_id_, scid, dcid);
   next_signal_id_++;
   pending_commands_.push(pending_command);
   if (pending_commands_.size() == 1) {
@@ -78,7 +89,13 @@
 
 void LeSignallingManager::SendConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max,
                                                                uint16_t slave_latency, uint16_t timeout_multiplier) {
-  LOG_ERROR("Not implemented");
+  PendingCommand pending_command = PendingCommand::ConnectionParameterUpdate(
+      next_signal_id_, interval_min, interval_max, slave_latency, timeout_multiplier);
+  next_signal_id_++;
+  pending_commands_.push(pending_command);
+  if (pending_commands_.size() == 1) {
+    handle_send_next_command();
+  }
 }
 
 void LeSignallingManager::SendConnectionParameterUpdateResponse(SignalId signal_id,
@@ -93,25 +110,61 @@
   enqueue_buffer_->Enqueue(std::move(builder), handler_);
 }
 
+void LeSignallingManager::CancelAlarm() {
+  alarm_.Cancel();
+}
+
 void LeSignallingManager::OnCommandReject(LeCommandRejectView command_reject_view) {
   auto signal_id = command_reject_view.GetIdentifier();
-  if (signal_id != command_just_sent_.signal_id_ || command_just_sent_.command_code_ != command_reject_view.GetCode()) {
+  if (signal_id != command_just_sent_.signal_id_) {
     LOG_WARN("Unexpected response: no pending request");
     return;
   }
   alarm_.Cancel();
+  if (command_just_sent_.command_code_ == LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST) {
+    link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_,
+                                           LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
+  }
   handle_send_next_command();
 
   LOG_WARN("Command rejected");
 }
 
-void LeSignallingManager::OnConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max,
-                                                             uint16_t slave_latency, uint16_t timeout_multiplier) {
-  LOG_ERROR("Not implemented");
+void LeSignallingManager::OnConnectionParameterUpdateRequest(SignalId signal_id, uint16_t interval_min,
+                                                             uint16_t interval_max, uint16_t slave_latency,
+                                                             uint16_t timeout_multiplier) {
+  if (link_->GetRole() == hci::Role::SLAVE) {
+    LOG_WARN("Received request from LL master");
+    auto builder = LeCommandRejectNotUnderstoodBuilder::Create(signal_id.Value());
+    enqueue_buffer_->Enqueue(std::move(builder), handler_);
+    return;
+  }
+  if (interval_min < 6 || interval_min > 3200 || interval_max < 6 || interval_max > 3200 || slave_latency >= 500 ||
+      timeout_multiplier < 10 || timeout_multiplier > 3200) {
+    LOG_WARN("Received invalid connection parameter update request from LL master");
+    auto builder = ConnectionParameterUpdateResponseBuilder::Create(signal_id.Value(),
+                                                                    ConnectionParameterUpdateResponseResult::REJECTED);
+    enqueue_buffer_->Enqueue(std::move(builder), handler_);
+    return;
+  }
+  link_->UpdateConnectionParameterFromRemote(signal_id, interval_min, interval_max, slave_latency, timeout_multiplier);
 }
 
-void LeSignallingManager::OnConnectionParameterUpdateResponse(ConnectionParameterUpdateResponseResult result) {
-  LOG_ERROR("Not implemented");
+void LeSignallingManager::OnConnectionParameterUpdateResponse(SignalId signal_id,
+                                                              ConnectionParameterUpdateResponseResult result) {
+  if (signal_id != command_just_sent_.signal_id_) {
+    LOG_WARN("Unexpected response: no pending request");
+    return;
+  }
+  if (command_just_sent_.command_code_ != LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST) {
+    LOG_WARN("Unexpected response: no pending request");
+    return;
+  }
+  alarm_.Cancel();
+  command_just_sent_.signal_id_ = kInitialSignalId;
+  if (result != ConnectionParameterUpdateResponseResult::ACCEPTED) {
+    LOG_ERROR("Connection parameter update is not accepted");
+  }
 }
 
 void LeSignallingManager::OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid, Mtu mtu, uint16_t mps,
@@ -144,27 +197,68 @@
     return;
   }
 
+  PendingConnection pending{
+      .remote_cid = remote_cid,
+      .incoming_signal_id = signal_id,
+      .initial_credits = initial_credits,
+      .max_pdu_size = mps,
+      .mtu = mtu,
+  };
+  dynamic_service_manager_->GetSecurityEnforcementInterface()->Enforce(
+      link_->GetDevice(),
+      dynamic_service_manager_->GetService(psm)->GetSecurityPolicy(),
+      handler_->BindOnceOn(this, &LeSignallingManager::on_security_result_for_incoming, psm, pending));
+}
+
+void LeSignallingManager::on_security_result_for_incoming(Psm psm, PendingConnection request, bool result) {
+  auto signal_id = request.incoming_signal_id;
   auto* service = dynamic_service_manager_->GetService(psm);
+  if (!result) {
+    auto security_policy = service->GetSecurityPolicy();
+    switch (security_policy) {
+      case SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK:
+        LOG_ERROR("If no security requirement, we should never fail");
+        break;
+      case SecurityPolicy::ENCRYPTED_TRANSPORT:
+        send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
+                                 LeCreditBasedConnectionResponseResult::INSUFFICIENT_AUTHENTICATION);
+        return;
+      case SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT:
+      case SecurityPolicy::BEST:
+        send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
+                                 LeCreditBasedConnectionResponseResult::INSUFFICIENT_AUTHENTICATION);
+        return;
+      case SecurityPolicy::_NOT_FOR_YOU__AUTHENTICATED_PAIRING_WITH_128_BIT_KEY:
+        send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
+                                 LeCreditBasedConnectionResponseResult::INSUFFICIENT_ENCRYPTION_KEY_SIZE);
+        return;
+      case SecurityPolicy::_NOT_FOR_YOU__AUTHORIZATION:
+        send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
+                                 LeCreditBasedConnectionResponseResult::INSUFFICIENT_AUTHORIZATION);
+        return;
+    }
+  }
   auto config = service->GetConfigOption();
   auto local_mtu = config.mtu;
   auto local_mps = link_->GetMps();
 
-  auto new_channel = link_->AllocateDynamicChannel(psm, remote_cid, {});
+  auto new_channel = link_->AllocateDynamicChannel(psm, request.remote_cid);
   if (new_channel == nullptr) {
     LOG_WARN("Can't allocate dynamic channel");
+    // TODO: We need to respond with the correct reason
     send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
-                             LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
-
+                             LeCreditBasedConnectionResponseResult::SOURCE_CID_ALREADY_ALLOCATED);
     return;
   }
-  send_connection_response(signal_id, remote_cid, local_mtu, local_mps, link_->GetInitialCredit(),
+
+  send_connection_response(signal_id, new_channel->GetCid(), local_mtu, local_mps, link_->GetInitialCredit(),
                            LeCreditBasedConnectionResponseResult::SUCCESS);
   auto* data_controller = reinterpret_cast<l2cap::internal::LeCreditBasedDataController*>(
       data_pipeline_manager_->GetDataController(new_channel->GetCid()));
-  data_controller->SetMtu(std::min(mtu, local_mtu));
-  data_controller->SetMps(std::min(mps, local_mps));
-  data_controller->OnCredit(initial_credits);
-  auto user_channel = std::make_unique<DynamicChannel>(new_channel, handler_);
+  data_controller->SetMtu(std::min(request.mtu, local_mtu));
+  data_controller->SetMps(std::min(request.max_pdu_size, local_mps));
+  data_controller->OnCredit(request.initial_credits);
+  auto user_channel = std::make_unique<DynamicChannel>(new_channel, handler_, link_);
   dynamic_service_manager_->GetService(psm)->NotifyChannelCreation(std::move(user_channel));
 }
 
@@ -182,15 +276,16 @@
   command_just_sent_.signal_id_ = kInitialSignalId;
   if (result != LeCreditBasedConnectionResponseResult::SUCCESS) {
     LOG_WARN("Connection failed: %s", LeCreditBasedConnectionResponseResultText(result).data());
-    link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+    link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_, result);
     handle_send_next_command();
     return;
   }
   auto new_channel =
-      link_->AllocateReservedDynamicChannel(command_just_sent_.source_cid_, command_just_sent_.psm_, remote_cid, {});
+      link_->AllocateReservedDynamicChannel(command_just_sent_.source_cid_, command_just_sent_.psm_, remote_cid);
   if (new_channel == nullptr) {
     LOG_WARN("Can't allocate dynamic channel");
-    link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+    link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_,
+                                           LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
     handle_send_next_command();
     return;
   }
@@ -199,7 +294,7 @@
   data_controller->SetMtu(std::min(mtu, command_just_sent_.mtu_));
   data_controller->SetMps(std::min(mps, command_just_sent_.mps_));
   data_controller->OnCredit(initial_credits);
-  std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(new_channel, handler_);
+  std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(new_channel, handler_, link_);
   dynamic_service_manager_->GetService(command_just_sent_.psm_)->NotifyChannelCreation(std::move(user_channel));
 }
 
@@ -219,7 +314,7 @@
   link_->FreeDynamicChannel(cid);
 }
 
-void LeSignallingManager::OnDisconnectionResponse(SignalId signal_id, Cid cid, Cid remote_cid) {
+void LeSignallingManager::OnDisconnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid) {
   if (signal_id != command_just_sent_.signal_id_ ||
       command_just_sent_.command_code_ != LeCommandCode::DISCONNECTION_REQUEST) {
     LOG_WARN("Unexpected response: no pending request");
@@ -281,8 +376,9 @@
         return;
       }
       OnConnectionParameterUpdateRequest(
-          parameter_update_req_view.GetIntervalMin(), parameter_update_req_view.GetIntervalMax(),
-          parameter_update_req_view.GetSlaveLatency(), parameter_update_req_view.GetTimeoutMultiplier());
+          parameter_update_req_view.GetIdentifier(), parameter_update_req_view.GetIntervalMin(),
+          parameter_update_req_view.GetIntervalMax(), parameter_update_req_view.GetSlaveLatency(),
+          parameter_update_req_view.GetTimeoutMultiplier());
       return;
     }
     case LeCommandCode::CONNECTION_PARAMETER_UPDATE_RESPONSE: {
@@ -291,7 +387,8 @@
       if (!parameter_update_rsp_view.IsValid()) {
         return;
       }
-      OnConnectionParameterUpdateResponse(parameter_update_rsp_view.GetResult());
+      OnConnectionParameterUpdateResponse(parameter_update_rsp_view.GetIdentifier(),
+                                          parameter_update_rsp_view.GetResult());
       return;
     }
     case LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST: {
@@ -368,7 +465,8 @@
   }
   switch (command_just_sent_.command_code_) {
     case LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST: {
-      link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+      link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_,
+                                             LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
       break;
     }
     default:
@@ -401,6 +499,14 @@
       alarm_.Schedule(common::BindOnce(&LeSignallingManager::on_command_timeout, common::Unretained(this)), kTimeout);
       break;
     }
+    case LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST: {
+      auto builder = ConnectionParameterUpdateRequestBuilder::Create(
+          command_just_sent_.signal_id_.Value(), command_just_sent_.interval_min_, command_just_sent_.interval_max_,
+          command_just_sent_.slave_latency_, command_just_sent_.timeout_multiplier_);
+      enqueue_buffer_->Enqueue(std::move(builder), handler_);
+      alarm_.Schedule(common::BindOnce(&LeSignallingManager::on_command_timeout, common::Unretained(this)), kTimeout);
+      break;
+    }
     default: {
       LOG_WARN("Unsupported command code 0x%x", static_cast<int>(command_just_sent_.command_code_));
     }
diff --git a/gd/l2cap/le/internal/signalling_manager.h b/gd/l2cap/le/internal/signalling_manager.h
index 74e85ce..ff99fb5 100644
--- a/gd/l2cap/le/internal/signalling_manager.h
+++ b/gd/l2cap/le/internal/signalling_manager.h
@@ -49,6 +49,44 @@
   Mtu mtu_;
   uint16_t mps_;
   uint16_t credits_;
+  uint16_t interval_min_;
+  uint16_t interval_max_;
+  uint16_t slave_latency_;
+  uint16_t timeout_multiplier_;
+
+  static PendingCommand CreditBasedConnectionRequest(SignalId signal_id, Psm psm, Cid scid, Mtu mtu, uint16_t mps,
+                                                     uint16_t initial_credits) {
+    PendingCommand pending_command;
+    pending_command.signal_id_ = signal_id;
+    pending_command.command_code_ = LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST;
+    pending_command.psm_ = psm;
+    pending_command.source_cid_ = scid;
+    pending_command.mtu_ = mtu;
+    pending_command.mps_ = mps;
+    pending_command.credits_ = initial_credits;
+    return pending_command;
+  }
+
+  static PendingCommand DisconnectionRequest(SignalId signal_id, Cid scid, Cid dcid) {
+    PendingCommand pending_command;
+    pending_command.signal_id_ = signal_id;
+    pending_command.command_code_ = LeCommandCode::DISCONNECTION_REQUEST;
+    pending_command.source_cid_ = scid;
+    pending_command.destination_cid_ = dcid;
+    return pending_command;
+  }
+
+  static PendingCommand ConnectionParameterUpdate(SignalId signal_id, uint16_t interval_min, uint16_t interval_max,
+                                                  uint16_t slave_latency, uint16_t timeout_multiplier) {
+    PendingCommand pending_command;
+    pending_command.signal_id_ = signal_id;
+    pending_command.command_code_ = LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST;
+    pending_command.interval_min_ = interval_min;
+    pending_command.interval_max_ = interval_max;
+    pending_command.slave_latency_ = slave_latency;
+    pending_command.timeout_multiplier_ = timeout_multiplier;
+    return pending_command;
+  }
 };
 
 class Link;
@@ -65,6 +103,7 @@
 
   void SendDisconnectRequest(Cid local_cid, Cid remote_cid);
 
+  // Note: Since Core 4.1, LL slave can send this through HCI command.
   void SendConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency,
                                             uint16_t timeout_multiplier);
 
@@ -72,11 +111,13 @@
 
   void SendCredit(Cid local_cid, uint16_t credits);
 
+  void CancelAlarm();
+
   void OnCommandReject(LeCommandRejectView command_reject_view);
 
-  void OnConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency,
-                                          uint16_t timeout_multiplier);
-  void OnConnectionParameterUpdateResponse(ConnectionParameterUpdateResponseResult result);
+  void OnConnectionParameterUpdateRequest(SignalId signal_id, uint16_t interval_min, uint16_t interval_max,
+                                          uint16_t slave_latency, uint16_t timeout_multiplier);
+  void OnConnectionParameterUpdateResponse(SignalId signal_id, ConnectionParameterUpdateResponseResult result);
 
   void OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid, Mtu mtu, uint16_t mps,
                            uint16_t initial_credits);
@@ -91,11 +132,21 @@
   void OnCredit(Cid remote_cid, uint16_t credits);
 
  private:
+  struct PendingConnection {
+    Cid remote_cid;
+    Mtu mtu;
+    uint16_t max_pdu_size;
+    uint16_t initial_credits;
+    SignalId incoming_signal_id;
+  };
+
   void on_incoming_packet();
   void send_connection_response(SignalId signal_id, Cid local_cid, Mtu mtu, uint16_t mps, uint16_t initial_credit,
                                 LeCreditBasedConnectionResponseResult result);
   void on_command_timeout();
   void handle_send_next_command();
+  void on_security_result_for_incoming(Psm psm, PendingConnection request, bool result);
+  void on_security_result_for_outgoing(Psm psm, Cid local_cid, Mtu mtu, bool result);
 
   os::Handler* handler_;
   Link* link_;
diff --git a/gd/l2cap/le/l2cap_le_module.cc b/gd/l2cap/le/l2cap_le_module.cc
index 4959547..97855f4 100644
--- a/gd/l2cap/le/l2cap_le_module.cc
+++ b/gd/l2cap/le/l2cap_le_module.cc
@@ -13,21 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#define LOG_TAG "l2cap2"
 
 #include <memory>
 
-#include "common/bidi_queue.h"
 #include "hci/acl_manager.h"
-#include "hci/address.h"
-#include "hci/hci_layer.h"
-#include "hci/hci_packets.h"
 #include "l2cap/internal/parameter_provider.h"
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
 #include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
 #include "l2cap/le/internal/link_manager.h"
+#include "l2cap/le/security_enforcement_interface.h"
 #include "module.h"
 #include "os/handler.h"
-#include "os/log.h"
 
 #include "l2cap/le/l2cap_le_module.h"
 
@@ -37,15 +33,20 @@
 
 const ModuleFactory L2capLeModule::Factory = ModuleFactory([]() { return new L2capLeModule(); });
 
+static SecurityEnforcementRejectAllImpl default_security_module_impl_;
+
 struct L2capLeModule::impl {
   impl(os::Handler* l2cap_handler, hci::AclManager* acl_manager)
-      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager) {}
+      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager) {
+    dynamic_channel_service_manager_impl_.SetSecurityEnforcementInterface(&default_security_module_impl_);
+  }
   os::Handler* l2cap_handler_;
   hci::AclManager* acl_manager_;
   l2cap::internal::ParameterProvider parameter_provider_;
   internal::FixedChannelServiceManagerImpl fixed_channel_service_manager_impl_{l2cap_handler_};
+  internal::DynamicChannelServiceManagerImpl dynamic_channel_service_manager_impl_{l2cap_handler_};
   internal::LinkManager link_manager_{l2cap_handler_, acl_manager_, &fixed_channel_service_manager_impl_,
-                                      &parameter_provider_};
+                                      &dynamic_channel_service_manager_impl_, &parameter_provider_};
 };
 
 L2capLeModule::L2capLeModule() {}
@@ -72,6 +73,19 @@
                                                                       &pimpl_->link_manager_, pimpl_->l2cap_handler_));
 }
 
+std::unique_ptr<DynamicChannelManager> L2capLeModule::GetDynamicChannelManager() {
+  return std::unique_ptr<DynamicChannelManager>(new DynamicChannelManager(
+      &pimpl_->dynamic_channel_service_manager_impl_, &pimpl_->link_manager_, pimpl_->l2cap_handler_));
+}
+
+void L2capLeModule::InjectSecurityEnforcementInterface(SecurityEnforcementInterface* security_enforcement_interface) {
+  if (security_enforcement_interface != nullptr) {
+    pimpl_->dynamic_channel_service_manager_impl_.SetSecurityEnforcementInterface(security_enforcement_interface);
+  } else {
+    pimpl_->dynamic_channel_service_manager_impl_.SetSecurityEnforcementInterface(&default_security_module_impl_);
+  }
+}
+
 }  // namespace le
 }  // namespace l2cap
 }  // namespace bluetooth
diff --git a/gd/l2cap/le/l2cap_le_module.h b/gd/l2cap/le/l2cap_le_module.h
index 4ed3d9f..43df7db 100644
--- a/gd/l2cap/le/l2cap_le_module.h
+++ b/gd/l2cap/le/l2cap_le_module.h
@@ -17,10 +17,17 @@
 
 #include <memory>
 
+#include "l2cap/le/dynamic_channel_manager.h"
 #include "l2cap/le/fixed_channel_manager.h"
+#include "l2cap/le/security_enforcement_interface.h"
 #include "module.h"
 
 namespace bluetooth {
+
+namespace security {
+class SecurityModule;
+}
+
 namespace l2cap {
 namespace le {
 
@@ -34,6 +41,11 @@
    */
   virtual std::unique_ptr<FixedChannelManager> GetFixedChannelManager();
 
+  /**
+   * Get the api to the LE dynamic channel l2cap module
+   */
+  virtual std::unique_ptr<DynamicChannelManager> GetDynamicChannelManager();
+
   static const ModuleFactory Factory;
 
  protected:
@@ -48,6 +60,14 @@
  private:
   struct impl;
   std::unique_ptr<impl> pimpl_;
+
+  friend security::SecurityModule;
+  /**
+   * Only for the LE security module to inject functionality to enforce security level for a connection. When LE
+   * security module is stopping, inject nullptr. Note: We expect this only to be called during stack startup. This is
+   * not synchronized.
+   */
+  virtual void InjectSecurityEnforcementInterface(SecurityEnforcementInterface* security_enforcement_interface);
   DISALLOW_COPY_AND_ASSIGN(L2capLeModule);
 };
 
diff --git a/gd/l2cap/le/link_options.cc b/gd/l2cap/le/link_options.cc
new file mode 100644
index 0000000..adf74ee
--- /dev/null
+++ b/gd/l2cap/le/link_options.cc
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#include "l2cap/le/link_options.h"
+
+#include <cstdint>
+
+#include "hci/hci_packets.h"
+#include "l2cap/le/internal/link.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+LinkOptions::LinkOptions(hci::acl_manager::LeAclConnection* acl_connection, internal::Link* link,
+                         os::Handler* l2cap_handler)
+    : acl_connection_(acl_connection), link_(link), l2cap_handler_(l2cap_handler) {}
+
+hci::Role LinkOptions::GetRole() const {
+  return acl_connection_->GetRole();
+}
+
+uint16_t LinkOptions::GetHandle() const {
+  return acl_connection_->GetHandle();
+}
+
+hci::AddressWithType LinkOptions::GetLocalAddress() const {
+  return acl_connection_->GetLocalAddress();
+}
+
+bool LinkOptions::UpdateConnectionParameter(uint16_t conn_interval_min, uint16_t conn_interval_max,
+                                            uint16_t conn_latency, uint16_t supervision_timeout, uint16_t min_ce_length,
+                                            uint16_t max_ce_length) {
+  if (conn_interval_min < 0x0006 || conn_interval_min > 0x0C80 || conn_interval_max < 0x0006 ||
+      conn_interval_max > 0x0C80 || conn_latency > 0x01F3 || supervision_timeout < 0x000A ||
+      supervision_timeout > 0x0C80) {
+    LOG_ERROR("Invalid parameter");
+    return false;
+  }
+
+  l2cap_handler_->Post(common::BindOnce(&internal::Link::SendConnectionParameterUpdate, common::Unretained(link_),
+                                        conn_interval_min, conn_interval_max, conn_latency, supervision_timeout,
+                                        min_ce_length, max_ce_length));
+
+  return true;
+}
+
+bool LinkOptions::SetPhy(uint8_t all_phys, uint8_t tx_phys, uint8_t rx_phys, uint16_t phy_options) {
+  LOG_ERROR("Not implemented");
+  return false;
+}
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/link_options.h b/gd/l2cap/le/link_options.h
new file mode 100644
index 0000000..d7326df
--- /dev/null
+++ b/gd/l2cap/le/link_options.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include "hci/acl_manager/le_acl_connection.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+class Link;
+}
+
+/**
+ * Proxy for L2CAP user to get some link layer properties (connection handle, role), and set link layer options
+ * (connection parameter update, set PHY). Only few special L2CAP users need to use it, including Security Manager,
+ * Hearing Aid Profile, HID Profile, and Java API.
+ * Note: Setting link layer options applies to the LINK, not single CHANNEL.
+ */
+class LinkOptions {
+ public:
+  /**
+   * Get LL Role. Most applications should NOT know its LL role.
+   */
+  hci::Role GetRole() const;
+
+  /**
+   * Get ACL Handle. Most applications should NOT know its ACL handle.
+   */
+  uint16_t GetHandle() const;
+
+  /**
+   * Return Local address used for initiation of this connection.
+   */
+  hci::AddressWithType GetLocalAddress() const;
+
+  /**
+   * Update the LE link layer connection parameters.
+   * Depending on the link role and supported features, may directly send HCI command to update link, or send L2CAP
+   * request to advise the remote. The updated connection parameters are still determined by controller. It's a link
+   * layer change for performance tuning, and no host layer change should be observable by user.
+   * Parameters are defined in Core spec HCI 7.8.18.
+   * @return true iff the request is sent to controller through HCI or remote through L2CAP
+   * (Use it only if you know what you are doing!)
+   */
+  bool UpdateConnectionParameter(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
+                                 uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length);
+
+  /**
+   * Set PHY preference. The PHY is determined by the controller.
+   * No host layer change should be observable by user.
+   * Parameters are defined in Core spec HCI 7.8.49.
+   * @return true iff the request is sent to controller through HCI
+   * (Use it only if you know what you are doing!)
+   */
+  bool SetPhy(uint8_t all_phys, uint8_t tx_phys, uint8_t rx_phys, uint16_t phy_options);
+
+  LinkOptions(hci::acl_manager::LeAclConnection* acl_connection, internal::Link* link, os::Handler* l2cap_handler);
+
+ private:
+  hci::acl_manager::LeAclConnection* acl_connection_ = nullptr;
+  internal::Link* link_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+};
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/security_enforcement_interface.h b/gd/l2cap/le/security_enforcement_interface.h
new file mode 100644
index 0000000..0545d15
--- /dev/null
+++ b/gd/l2cap/le/security_enforcement_interface.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "common/contextual_callback.h"
+#include "hci/address_with_type.h"
+#include "l2cap/le/security_policy.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+/**
+ * The interface for Security Module to implement.
+ */
+class SecurityEnforcementInterface {
+ public:
+  virtual ~SecurityEnforcementInterface() = default;
+
+  using ResultCallback = common::ContextualOnceCallback<void(bool)>;
+
+  /**
+   * Invoked when L2CAP needs to open a channel with given security requirement. When the Security Module satisfies the
+   * required security level, or cannot satisfy at all, invoke the result_callback.
+   */
+  virtual void Enforce(hci::AddressWithType remote, SecurityPolicy policy, ResultCallback result_callback) = 0;
+};
+
+/**
+ * A default implementation which cannot satisfy any security level except
+ * NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK.
+ */
+class SecurityEnforcementRejectAllImpl : public SecurityEnforcementInterface {
+ public:
+  void Enforce(hci::AddressWithType remote, SecurityPolicy policy, ResultCallback result_callback) override {
+    if (policy == SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK) {
+      result_callback.InvokeIfNotEmpty(true);
+    } else {
+      result_callback.InvokeIfNotEmpty(false);
+    }
+  }
+};
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/security_policy.h b/gd/l2cap/le/security_policy.h
new file mode 100644
index 0000000..d3f7b57
--- /dev/null
+++ b/gd/l2cap/le/security_policy.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+enum class SecurityPolicy {
+  // Predefined security policies for user to pick
+
+  // No security enforced
+  NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK,
+
+  // Just encryption, but no MITM
+  ENCRYPTED_TRANSPORT,
+
+  // Implicitly MITM protected
+  AUTHENTICATED_ENCRYPTED_TRANSPORT,
+
+  // Same as AUTHENTICATED_ENCRYPTED_TRANSPORT
+  BEST,
+
+  _NOT_FOR_YOU__AUTHENTICATED_PAIRING_WITH_128_BIT_KEY,
+  _NOT_FOR_YOU__AUTHORIZATION,
+};
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/pts/run_pts_l2cap.sh b/gd/l2cap/pts/run_pts_l2cap.sh
deleted file mode 100755
index 9165f58..0000000
--- a/gd/l2cap/pts/run_pts_l2cap.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/bash
-
-act.py -c $ANDROID_BUILD_TOP/system/bt/gd/l2cap/pts/pts.json -tf $ANDROID_BUILD_TOP/system/bt/gd/l2cap/pts/pts_l2cap_testcase -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/module.cc b/gd/module.cc
index f12da97..2d3c031 100644
--- a/gd/module.cc
+++ b/gd/module.cc
@@ -21,7 +21,7 @@
 
 namespace bluetooth {
 
-constexpr std::chrono::milliseconds kModuleStopTimeout = std::chrono::milliseconds(20);
+constexpr std::chrono::milliseconds kModuleStopTimeout = std::chrono::milliseconds(2000);
 
 ModuleFactory::ModuleFactory(std::function<Module*()> ctor) : ctor_(ctor) {
 }
@@ -35,6 +35,11 @@
   return handler_;
 }
 
+DumpsysDataFinisher EmptyDumpsysDataFinisher = [](DumpsysDataBuilder* dumpsys_data_builder) {};
+DumpsysDataFinisher Module::GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const {
+  return EmptyDumpsysDataFinisher;
+}
+
 const ModuleRegistry* Module::GetModuleRegistry() const {
   return registry_;
 }
@@ -95,10 +100,15 @@
     ASSERT(instance != started_modules_.end());
 
     // Clear the handler before stopping the module to allow it to shut down gracefully.
+    LOG_INFO("Stopping Handler of Module %s", instance->second->ToString().c_str());
     instance->second->handler_->Clear();
     instance->second->handler_->WaitUntilStopped(kModuleStopTimeout);
+    LOG_INFO("Stopping Module %s", instance->second->ToString().c_str());
     instance->second->Stop();
-
+  }
+  for (auto it = start_order_.rbegin(); it != start_order_.rend(); it++) {
+    auto instance = started_modules_.find(*it);
+    ASSERT(instance != started_modules_.end());
     delete instance->second->handler_;
     delete instance->second;
     started_modules_.erase(instance);
@@ -115,4 +125,30 @@
   }
   return nullptr;
 }
+
+void ModuleDumper::DumpState(std::string* output) const {
+  ASSERT(output != nullptr);
+
+  flatbuffers::FlatBufferBuilder builder(1024);
+  auto title = builder.CreateString(title_);
+
+  std::queue<DumpsysDataFinisher> queue;
+  for (auto it = module_registry_.start_order_.rbegin(); it != module_registry_.start_order_.rend(); it++) {
+    auto instance = module_registry_.started_modules_.find(*it);
+    ASSERT(instance != module_registry_.started_modules_.end());
+    queue.push(instance->second->GetDumpsysData(&builder));
+  }
+
+  DumpsysDataBuilder data_builder(builder);
+  data_builder.add_title(title);
+
+  while (!queue.empty()) {
+    queue.front()(&data_builder);
+    queue.pop();
+  }
+
+  builder.Finish(data_builder.Finish());
+  *output = std::string(builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize());
+}
+
 }  // namespace bluetooth
diff --git a/gd/module.h b/gd/module.h
index 045865d..40ee854 100644
--- a/gd/module.h
+++ b/gd/module.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <flatbuffers/flatbuffers.h>
 #include <functional>
 #include <future>
 #include <map>
@@ -23,6 +24,7 @@
 #include <vector>
 
 #include "common/bind.h"
+#include "dumpsys_data_generated.h"
 #include "os/handler.h"
 #include "os/log.h"
 #include "os/thread.h"
@@ -30,30 +32,38 @@
 namespace bluetooth {
 
 class Module;
+class ModuleDumper;
 class ModuleRegistry;
+class TestModuleRegistry;
+class FuzzTestModuleRegistry;
 
 class ModuleFactory {
  friend ModuleRegistry;
- public:
-  ModuleFactory(std::function<Module*()> ctor);
+ friend FuzzTestModuleRegistry;
 
- private:
-  std::function<Module*()> ctor_;
+public:
+ ModuleFactory(std::function<Module*()> ctor);
+
+private:
+ std::function<Module*()> ctor_;
 };
 
 class ModuleList {
- friend ModuleRegistry;
  friend Module;
- public:
-  template <class T>
-  void add() {
-    list_.push_back(&T::Factory);
-  }
+ friend ModuleRegistry;
+
+public:
+ template <class T>
+ void add() {
+   list_.push_back(&T::Factory);
+ }
 
  private:
   std::vector<const ModuleFactory*> list_;
 };
 
+using DumpsysDataFinisher = std::function<void(DumpsysDataBuilder* dumpsys_data_builder)>;
+
 // Each leaf node module must have a factory like so:
 //
 // static const ModuleFactory Factory;
@@ -62,7 +72,10 @@
 // The module registry will also use the factory as the identifier
 // for that module.
 class Module {
- friend ModuleRegistry;
+  friend ModuleDumper;
+  friend ModuleRegistry;
+  friend TestModuleRegistry;
+
  public:
   virtual ~Module() = default;
  protected:
@@ -76,6 +89,9 @@
   // Release all resources, you're about to be deleted
   virtual void Stop() = 0;
 
+  // Get relevant state data from the module
+  virtual DumpsysDataFinisher GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const;
+
   virtual std::string ToString() const;
 
   ::bluetooth::os::Handler* GetHandler() const;
@@ -87,6 +103,16 @@
     return static_cast<T*>(GetDependency(&T::Factory));
   }
 
+  template <typename Functor, typename... Args>
+  void Call(Functor&& functor, Args&&... args) {
+    GetHandler()->Call(std::forward<Functor>(functor), std::forward<Args>(args)...);
+  }
+
+  template <typename T, typename Functor, typename... Args>
+  void CallOn(T* obj, Functor&& functor, Args&&... args) {
+    GetHandler()->CallOn(obj, std::forward<Functor>(functor), std::forward<Args>(args)...);
+  }
+
  private:
   Module* GetDependency(const ModuleFactory* module) const;
 
@@ -97,6 +123,7 @@
 
 class ModuleRegistry {
  friend Module;
+ friend ModuleDumper;
  friend class StackManager;
  public:
   template <class T>
@@ -131,18 +158,35 @@
   std::vector<const ModuleFactory*> start_order_;
 };
 
+class ModuleDumper {
+ public:
+  ModuleDumper(const ModuleRegistry& module_registry, const char* title)
+      : module_registry_(module_registry), title_(title) {}
+  void DumpState(std::string* output) const;
+
+ private:
+  const ModuleRegistry& module_registry_;
+  const std::string title_;
+};
+
 class TestModuleRegistry : public ModuleRegistry {
  public:
   void InjectTestModule(const ModuleFactory* module, Module* instance) {
     start_order_.push_back(module);
     started_modules_[module] = instance;
     set_registry_and_handler(instance, &test_thread);
+    instance->Start();
   }
 
   Module* GetModuleUnderTest(const ModuleFactory* module) const {
     return Get(module);
   }
 
+  template <class T>
+  T* GetModuleUnderTest() const {
+    return static_cast<T*>(GetModuleUnderTest(&T::Factory));
+  }
+
   os::Handler* GetTestModuleHandler(const ModuleFactory* module) const {
     return GetModuleHandler(module);
   }
@@ -152,9 +196,12 @@
   }
 
   bool SynchronizeModuleHandler(const ModuleFactory* module, std::chrono::milliseconds timeout) const {
+    return SynchronizeHandler(GetTestModuleHandler(module), timeout);
+  }
+
+  bool SynchronizeHandler(os::Handler* handler, std::chrono::milliseconds timeout) const {
     std::promise<void> promise;
     auto future = promise.get_future();
-    os::Handler* handler = GetTestModuleHandler(module);
     handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
     return future.wait_for(timeout) == std::future_status::ready;
   }
@@ -163,4 +210,26 @@
   os::Thread test_thread{"test_thread", os::Thread::Priority::NORMAL};
 };
 
+class FuzzTestModuleRegistry : public TestModuleRegistry {
+ public:
+  template <class T>
+  T* Inject(const ModuleFactory* overriding) {
+    Module* instance = T::Factory.ctor_();
+    InjectTestModule(overriding, instance);
+    return static_cast<T*>(instance);
+  }
+
+  template <class T>
+  T* Start() {
+    return ModuleRegistry::Start<T>(&GetTestThread());
+  }
+
+  void WaitForIdleAndStopAll() {
+    if (!GetTestThread().GetReactor()->WaitForIdle(std::chrono::milliseconds(100))) {
+      LOG_ERROR("idle timed out");
+    }
+    StopAll();
+  }
+};
+
 }  // namespace bluetooth
diff --git a/gd/module_unittest.cc b/gd/module_unittest.cc
index d0efb5d..b0b35db 100644
--- a/gd/module_unittest.cc
+++ b/gd/module_unittest.cc
@@ -15,9 +15,15 @@
  */
 
 #include "module.h"
+#include "module_unittest_generated.h"
+#include "os/handler.h"
+#include "os/thread.h"
 
 #include "gtest/gtest.h"
 
+#include <functional>
+#include <future>
+
 using ::bluetooth::os::Thread;
 
 namespace bluetooth {
@@ -39,6 +45,8 @@
   Thread* thread_;
 };
 
+os::Handler* test_module_no_dependency_handler = nullptr;
+
 class TestModuleNoDependency : public Module {
  public:
   static const ModuleFactory Factory;
@@ -50,6 +58,7 @@
   void Start() override {
     // A module is not considered started until Start() finishes
     EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+    test_module_no_dependency_handler = GetHandler();
   }
 
   void Stop() override {
@@ -62,6 +71,8 @@
   return new TestModuleNoDependency();
 });
 
+os::Handler* test_module_one_dependency_handler = nullptr;
+
 class TestModuleOneDependency : public Module {
  public:
   static const ModuleFactory Factory;
@@ -76,6 +87,7 @@
 
     // A module is not considered started until Start() finishes
     EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleOneDependency>());
+    test_module_one_dependency_handler = GetHandler();
   }
 
   void Stop() override {
@@ -145,6 +157,47 @@
   return new TestModuleTwoDependencies();
 });
 
+// To generate module unittest flatbuffer headers:
+// $ flatc --cpp module_unittest.fbs
+class TestModuleDumpState : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+  std::string test_string_{"Initial Test String"};
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+    list->add<TestModuleNoDependency>();
+  }
+
+  void Start() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+
+    // A module is not considered started until Start() finishes
+    EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleDumpState>());
+    test_module_one_dependency_handler = GetHandler();
+  }
+
+  void Stop() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+
+    // A module is not considered stopped until after Stop() finishes
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleDumpState>());
+  }
+
+  DumpsysDataFinisher GetDumpsysData(flatbuffers::FlatBufferBuilder* fb_builder) const override {
+    auto string = fb_builder->CreateString(test_string_.c_str());
+
+    auto builder = ModuleUnitTestDataBuilder(*fb_builder);
+    builder.add_title(string);
+    auto table = builder.Finish();
+
+    return [table](DumpsysDataBuilder* builder) { builder->add_module_unittest_data(table); };
+  }
+};
+
+const ModuleFactory TestModuleDumpState::Factory = ModuleFactory([]() { return new TestModuleDumpState(); });
+
 TEST_F(ModuleTest, no_dependency) {
   ModuleList list;
   list.add<TestModuleNoDependency>();
@@ -199,5 +252,48 @@
   EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
 }
 
+void post_to_module_one_handler() {
+  std::this_thread::sleep_for(std::chrono::milliseconds(100));
+  test_module_one_dependency_handler->Post(common::BindOnce([] { FAIL(); }));
+}
+
+TEST_F(ModuleTest, shutdown_with_unhandled_callback) {
+  ModuleList list;
+  list.add<TestModuleOneDependency>();
+  registry_->Start(&list, thread_);
+  test_module_no_dependency_handler->Post(common::BindOnce(&post_to_module_one_handler));
+  registry_->StopAll();
+}
+
+TEST_F(ModuleTest, dump_state) {
+  static const char* title = "Test Dump Title";
+  ModuleList list;
+  list.add<TestModuleDumpState>();
+  registry_->Start(&list, thread_);
+
+  ModuleDumper dumper(*registry_, title);
+
+  std::string output;
+  dumper.DumpState(&output);
+
+  auto data = flatbuffers::GetRoot<DumpsysData>(output.data());
+  EXPECT_STREQ(title, data->title()->c_str());
+
+  auto test_data = data->module_unittest_data();
+  EXPECT_STREQ("Initial Test String", test_data->title()->c_str());
+
+  TestModuleDumpState* test_module =
+      static_cast<TestModuleDumpState*>(registry_->Start(&TestModuleDumpState::Factory, nullptr));
+  test_module->test_string_ = "A Second Test String";
+
+  dumper.DumpState(&output);
+
+  data = flatbuffers::GetRoot<DumpsysData>(output.data());
+  test_data = data->module_unittest_data();
+  EXPECT_STREQ("A Second Test String", test_data->title()->c_str());
+
+  registry_->StopAll();
+}
+
 }  // namespace
 }  // namespace bluetooth
diff --git a/gd/module_unittest.fbs b/gd/module_unittest.fbs
new file mode 100644
index 0000000..3c0a1b6
--- /dev/null
+++ b/gd/module_unittest.fbs
@@ -0,0 +1,10 @@
+// module_unittest
+namespace bluetooth;
+
+attribute "privacy";
+
+table ModuleUnitTestData {
+    title:string (privacy:"Any");
+}
+
+root_type ModuleUnitTestData;
diff --git a/gd/module_unittest_generated.h b/gd/module_unittest_generated.h
new file mode 100644
index 0000000..3c9fd36
--- /dev/null
+++ b/gd/module_unittest_generated.h
@@ -0,0 +1,84 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+#ifndef FLATBUFFERS_GENERATED_MODULEUNITTEST_BLUETOOTH_H_
+#define FLATBUFFERS_GENERATED_MODULEUNITTEST_BLUETOOTH_H_
+
+#include "flatbuffers/flatbuffers.h"
+
+namespace bluetooth {
+
+struct ModuleUnitTestData;
+struct ModuleUnitTestDataBuilder;
+
+struct ModuleUnitTestData FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef ModuleUnitTestDataBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_TITLE = 4 };
+  const flatbuffers::String* title() const {
+    return GetPointer<const flatbuffers::String*>(VT_TITLE);
+  }
+  bool Verify(flatbuffers::Verifier& verifier) const {
+    return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_TITLE) && verifier.VerifyString(title()) &&
+           verifier.EndTable();
+  }
+};
+
+struct ModuleUnitTestDataBuilder {
+  typedef ModuleUnitTestData Table;
+  flatbuffers::FlatBufferBuilder& fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_title(flatbuffers::Offset<flatbuffers::String> title) {
+    fbb_.AddOffset(ModuleUnitTestData::VT_TITLE, title);
+  }
+  explicit ModuleUnitTestDataBuilder(flatbuffers::FlatBufferBuilder& _fbb) : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  ModuleUnitTestDataBuilder& operator=(const ModuleUnitTestDataBuilder&);
+  flatbuffers::Offset<ModuleUnitTestData> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<ModuleUnitTestData>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<ModuleUnitTestData> CreateModuleUnitTestData(
+    flatbuffers::FlatBufferBuilder& _fbb, flatbuffers::Offset<flatbuffers::String> title = 0) {
+  ModuleUnitTestDataBuilder builder_(_fbb);
+  builder_.add_title(title);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<ModuleUnitTestData> CreateModuleUnitTestDataDirect(
+    flatbuffers::FlatBufferBuilder& _fbb, const char* title = nullptr) {
+  auto title__ = title ? _fbb.CreateString(title) : 0;
+  return bluetooth::CreateModuleUnitTestData(_fbb, title__);
+}
+
+inline const bluetooth::ModuleUnitTestData* GetModuleUnitTestData(const void* buf) {
+  return flatbuffers::GetRoot<bluetooth::ModuleUnitTestData>(buf);
+}
+
+inline const bluetooth::ModuleUnitTestData* GetSizePrefixedModuleUnitTestData(const void* buf) {
+  return flatbuffers::GetSizePrefixedRoot<bluetooth::ModuleUnitTestData>(buf);
+}
+
+inline bool VerifyModuleUnitTestDataBuffer(flatbuffers::Verifier& verifier) {
+  return verifier.VerifyBuffer<bluetooth::ModuleUnitTestData>(nullptr);
+}
+
+inline bool VerifySizePrefixedModuleUnitTestDataBuffer(flatbuffers::Verifier& verifier) {
+  return verifier.VerifySizePrefixedBuffer<bluetooth::ModuleUnitTestData>(nullptr);
+}
+
+inline void FinishModuleUnitTestDataBuffer(
+    flatbuffers::FlatBufferBuilder& fbb, flatbuffers::Offset<bluetooth::ModuleUnitTestData> root) {
+  fbb.Finish(root);
+}
+
+inline void FinishSizePrefixedModuleUnitTestDataBuffer(
+    flatbuffers::FlatBufferBuilder& fbb, flatbuffers::Offset<bluetooth::ModuleUnitTestData> root) {
+  fbb.FinishSizePrefixed(root);
+}
+
+}  // namespace bluetooth
+
+#endif  // FLATBUFFERS_GENERATED_MODULEUNITTEST_BLUETOOTH_H_
diff --git a/gd/neighbor/Android.bp b/gd/neighbor/Android.bp
index bd69a3e..35ec925 100644
--- a/gd/neighbor/Android.bp
+++ b/gd/neighbor/Android.bp
@@ -5,6 +5,7 @@
             "discoverability.cc",
             "inquiry.cc",
             "name.cc",
+            "name_db.cc",
             "page.cc",
             "scan.cc",
     ],
@@ -17,3 +18,9 @@
     ],
 }
 
+filegroup {
+    name: "BluetoothFacade_neighbor",
+    srcs: [
+        "facade/facade.cc",
+    ],
+}
diff --git a/gd/neighbor/cert/neighbor_test.py b/gd/neighbor/cert/neighbor_test.py
new file mode 100644
index 0000000..4080aaa
--- /dev/null
+++ b/gd/neighbor/cert/neighbor_test.py
@@ -0,0 +1,102 @@
+#!/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.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
+
+
+class NeighborTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(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_with_complete(
+            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)
+
+    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_with_complete(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_with_complete(
+            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_with_complete(
+            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_with_complete(
+            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_with_complete(
+            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/neighbor/cert/py_neighbor.py b/gd/neighbor/cert/py_neighbor.py
new file mode 100644
index 0000000..7b31a08
--- /dev/null
+++ b/gd/neighbor/cert/py_neighbor.py
@@ -0,0 +1,84 @@
+#!/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 datetime import timedelta
+
+from bluetooth_packets_python3 import hci_packets
+from cert.event_stream import EventStream
+from cert.event_stream import IEventStream
+from cert.closable import Closable
+from cert.closable import safeClose
+from cert.truth import assertThat
+from google.protobuf import empty_pb2 as empty_proto
+from hci.facade import facade_pb2 as hci_facade
+from neighbor.facade import facade_pb2 as neighbor_facade
+
+
+class InquirySession(Closable, IEventStream):
+
+    def __init__(self, device, inquiry_msg):
+        self.inquiry_event_stream = EventStream(device.neighbor.SetInquiryMode(inquiry_msg))
+
+    def get_event_queue(self):
+        return self.inquiry_event_stream.get_event_queue()
+
+    def close(self):
+        safeClose(self.inquiry_event_stream)
+
+
+class GetRemoteNameSession(Closable):
+
+    def __init__(self, device):
+        self.remote_name_stream = EventStream(device.neighbor.GetRemoteNameEvents(empty_proto.Empty()))
+
+    def verify_name(self, name):
+        assertThat(self.remote_name_stream).emits(lambda msg: bytes(name) in msg.name, timeout=timedelta(seconds=10))
+
+    def close(self):
+        safeClose(self.remote_name_stream)
+
+
+class PyNeighbor(object):
+
+    def __init__(self, device):
+        self.device = device
+        self.remote_host_supported_features_notification_registered = False
+
+    def set_inquiry_mode(self, inquiry_msg):
+        """
+        Set the inquiry mode and return a session which can be used for event queue assertion
+        """
+        return InquirySession(self.device, inquiry_msg)
+
+    def _register_remote_host_supported_features_notification(self):
+        """
+        REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION event will be sent when a device sends remote name request
+        """
+        if self.remote_host_supported_features_notification_registered:
+            return
+        msg = hci_facade.EventCodeMsg(code=int(hci_packets.EventCode.REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION))
+        self.device.hci.RegisterEventHandler(msg)
+        self.remote_host_supported_features_notification_registered = True
+
+    def get_remote_name(self, remote_address):
+        """
+        Get the remote name and return a session which can be used for event queue assertion
+        """
+        self._register_remote_host_supported_features_notification()
+        self.device.neighbor.ReadRemoteName(
+            neighbor_facade.RemoteNameRequestMsg(
+                address=remote_address.encode('utf8'), page_scan_repetition_mode=1, clock_offset=0x6855))
+        return GetRemoteNameSession(self.device)
diff --git a/gd/neighbor/connectability.cc b/gd/neighbor/connectability.cc
index 8a60bdd..d9f2d90 100644
--- a/gd/neighbor/connectability.cc
+++ b/gd/neighbor/connectability.cc
@@ -15,12 +15,13 @@
  */
 #define LOG_TAG "neighbor2"
 
+#include "neighbor/connectability.h"
+
 #include <memory>
 
 #include "hci/hci_layer.h"
 #include "hci/hci_packets.h"
 #include "module.h"
-#include "neighbor/connectability.h"
 #include "neighbor/scan.h"
 #include "os/handler.h"
 #include "os/log.h"
diff --git a/gd/neighbor/discoverability.cc b/gd/neighbor/discoverability.cc
index 3514caf..b5192a0 100644
--- a/gd/neighbor/discoverability.cc
+++ b/gd/neighbor/discoverability.cc
@@ -15,13 +15,14 @@
  */
 #define LOG_TAG "bt_gd_neigh"
 
+#include "neighbor/discoverability.h"
+
 #include <memory>
 
 #include "common/bind.h"
 #include "hci/hci_layer.h"
 #include "hci/hci_packets.h"
 #include "module.h"
-#include "neighbor/discoverability.h"
 #include "neighbor/scan.h"
 #include "os/handler.h"
 #include "os/log.h"
@@ -91,10 +92,10 @@
 
 void neighbor::DiscoverabilityModule::impl::StartDiscoverability(std::vector<hci::Lap>& laps) {
   ASSERT(laps.size() <= num_supported_iac_);
-  hci_layer_->EnqueueCommand(hci::WriteCurrentIacLapBuilder::Create(laps),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
-  hci_layer_->EnqueueCommand(hci::ReadCurrentIacLapBuilder::Create(),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::WriteCurrentIacLapBuilder::Create(laps), handler_->BindOnceOn(this, &impl::OnCommandComplete));
+  hci_layer_->EnqueueCommand(
+      hci::ReadCurrentIacLapBuilder::Create(), handler_->BindOnceOn(this, &impl::OnCommandComplete));
   scan_module_->SetInquiryScan();
 }
 
@@ -115,11 +116,11 @@
   scan_module_ = module_.GetDependency<neighbor::ScanModule>();
   handler_ = module_.GetHandler();
 
-  hci_layer_->EnqueueCommand(hci::ReadCurrentIacLapBuilder::Create(),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::ReadCurrentIacLapBuilder::Create(), handler_->BindOnceOn(this, &impl::OnCommandComplete));
 
-  hci_layer_->EnqueueCommand(hci::ReadNumberOfSupportedIacBuilder::Create(),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::ReadNumberOfSupportedIacBuilder::Create(), handler_->BindOnceOn(this, &impl::OnCommandComplete));
   LOG_DEBUG("Started discoverability module");
 }
 
diff --git a/gd/neighbor/facade/facade.cc b/gd/neighbor/facade/facade.cc
new file mode 100644
index 0000000..54d00e4
--- /dev/null
+++ b/gd/neighbor/facade/facade.cc
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "neighbor/facade/facade.h"
+
+#include <memory>
+
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/hci_packets.h"
+#include "neighbor/facade/facade.grpc.pb.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+namespace bluetooth {
+namespace neighbor {
+namespace facade {
+
+class NeighborFacadeService : public NeighborFacade::Service {
+ public:
+  NeighborFacadeService(
+      ConnectabilityModule* connectability_module,
+      DiscoverabilityModule* discoverability_module,
+      InquiryModule* inquiry_module,
+      NameModule* name_module,
+      PageModule*,
+      ScanModule* scan_module,
+      ::bluetooth::os::Handler* facade_handler)
+      : connectability_module_(connectability_module),
+        discoverability_module_(discoverability_module),
+        inquiry_module_(inquiry_module),
+        name_module_(name_module),
+        scan_module_(scan_module),
+        facade_handler_(facade_handler) {}
+
+  ::grpc::Status SetConnectability(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::neighbor::EnableMsg* request,
+      ::google::protobuf::Empty* response) override {
+    if (request->enabled()) {
+      connectability_module_->StartConnectability();
+    } else {
+      connectability_module_->StopConnectability();
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetDiscoverability(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::neighbor::DiscoverabilitiyMsg* request,
+      ::google::protobuf::Empty* response) override {
+    switch (request->mode()) {
+      case DiscoverabilityMode::OFF:
+        discoverability_module_->StopDiscoverability();
+        break;
+      case DiscoverabilityMode::LIMITED:
+        discoverability_module_->StartLimitedDiscoverability();
+        break;
+      case DiscoverabilityMode::GENERAL:
+        discoverability_module_->StartGeneralDiscoverability();
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unknown discoverability mode %d", static_cast<int>(request->mode()));
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetInquiryMode(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::neighbor::InquiryMsg* request,
+      ::grpc::ServerWriter<InquiryResultMsg>* writer) override {
+    inquiry_module_->RegisterCallbacks(inquiry_callbacks_);
+    switch (request->result_mode()) {
+      case ResultMode::STANDARD:
+        inquiry_module_->SetStandardInquiryResultMode();
+        break;
+      case ResultMode::RSSI:
+        inquiry_module_->SetInquiryWithRssiResultMode();
+        break;
+      case ResultMode::EXTENDED:
+        inquiry_module_->SetExtendedInquiryResultMode();
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unknown result mode %d", static_cast<int>(request->result_mode()));
+    }
+    switch (request->inquiry_mode()) {
+      case DiscoverabilityMode::OFF:
+        inquiry_module_->StopInquiry();
+        break;
+      case DiscoverabilityMode::LIMITED:
+        inquiry_module_->StartLimitedInquiry(request->length_1_28s(), request->max_results());
+        break;
+      case DiscoverabilityMode::GENERAL:
+        inquiry_module_->StartGeneralInquiry(request->length_1_28s(), request->max_results());
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unknown discoverability mode %d", static_cast<int>(request->inquiry_mode()));
+    }
+    return pending_events_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status ReadRemoteName(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::neighbor::RemoteNameRequestMsg* request,
+      ::google::protobuf::Empty* response) override {
+    hci::Address remote;
+    ASSERT(hci::Address::FromString(request->address(), remote));
+    hci::PageScanRepetitionMode mode;
+    switch (request->page_scan_repetition_mode()) {
+      case 0:
+        mode = hci::PageScanRepetitionMode::R0;
+        break;
+      case 1:
+        mode = hci::PageScanRepetitionMode::R1;
+        break;
+      case 2:
+        mode = hci::PageScanRepetitionMode::R2;
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unknown PageScanRepetition mode %d", static_cast<int>(request->page_scan_repetition_mode()));
+    }
+    name_module_->ReadRemoteNameRequest(
+        remote,
+        mode,
+        request->clock_offset(),
+        (request->clock_offset() != 0 ? hci::ClockOffsetValid::VALID : hci::ClockOffsetValid::INVALID),
+        common::Bind(&NeighborFacadeService::on_remote_name, common::Unretained(this)),
+        facade_handler_);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status GetRemoteNameEvents(
+      ::grpc::ServerContext* context,
+      const ::google::protobuf::Empty* request,
+      ::grpc::ServerWriter<RemoteNameResponseMsg>* writer) override {
+    return pending_remote_names_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status EnableInquiryScan(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::neighbor::EnableMsg* request,
+      ::google::protobuf::Empty* response) override {
+    if (request->enabled()) {
+      scan_module_->SetInquiryScan();
+    } else {
+      scan_module_->ClearInquiryScan();
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status EnablePageScan(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::neighbor::EnableMsg* request,
+      ::google::protobuf::Empty* response) override {
+    if (request->enabled()) {
+      scan_module_->SetPageScan();
+    } else {
+      scan_module_->ClearPageScan();
+    }
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  void on_incoming_inquiry_result(hci::EventPacketView view) {
+    InquiryResultMsg inquiry_result_msg;
+    inquiry_result_msg.set_packet(std::string(view.begin(), view.end()));
+    pending_events_.OnIncomingEvent(std::move(inquiry_result_msg));
+  }
+
+  void on_incoming_inquiry_complete(hci::ErrorCode status) {
+    InquiryResultMsg inquiry_result_msg;
+    inquiry_result_msg.set_packet(hci::ErrorCodeText(status));
+    pending_events_.OnIncomingEvent(std::move(inquiry_result_msg));
+  }
+
+  InquiryCallbacks inquiry_callbacks_{
+      .result = [this](hci::InquiryResultView view) { on_incoming_inquiry_result(view); },
+      .result_with_rssi = [this](hci::InquiryResultWithRssiView view) { on_incoming_inquiry_result(view); },
+      .extended_result = [this](hci::ExtendedInquiryResultView view) { on_incoming_inquiry_result(view); },
+      .complete = [this](hci::ErrorCode status) { on_incoming_inquiry_complete(status); }};
+
+  void on_remote_name(hci::ErrorCode status, hci::Address address, std::array<uint8_t, 248> name) {
+    RemoteNameResponseMsg response;
+    response.set_status(static_cast<int>(status));
+    response.set_address(address.ToString());
+    response.set_name(name.begin(), name.size());
+    pending_remote_names_.OnIncomingEvent(response);
+  }
+
+  ConnectabilityModule* connectability_module_;
+  DiscoverabilityModule* discoverability_module_;
+  InquiryModule* inquiry_module_;
+  NameModule* name_module_;
+  ScanModule* scan_module_;
+  ::bluetooth::os::Handler* facade_handler_;
+  ::bluetooth::grpc::GrpcEventQueue<InquiryResultMsg> pending_events_{"InquiryResponses"};
+  ::bluetooth::grpc::GrpcEventQueue<RemoteNameResponseMsg> pending_remote_names_{"RemoteNameResponses"};
+};
+
+void NeighborFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<ConnectabilityModule>();
+  list->add<DiscoverabilityModule>();
+  list->add<InquiryModule>();
+  list->add<NameModule>();
+  list->add<PageModule>();
+  list->add<ScanModule>();
+}
+
+void NeighborFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new NeighborFacadeService(
+      GetDependency<ConnectabilityModule>(),
+      GetDependency<DiscoverabilityModule>(),
+      GetDependency<InquiryModule>(),
+      GetDependency<NameModule>(),
+      GetDependency<PageModule>(),
+      GetDependency<ScanModule>(),
+      GetHandler());
+}
+
+void NeighborFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* NeighborFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory NeighborFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new NeighborFacadeModule(); });
+
+}  // namespace facade
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/hci/cert/cert.h b/gd/neighbor/facade/facade.h
similarity index 69%
copy from gd/hci/cert/cert.h
copy to gd/neighbor/facade/facade.h
index e3ecf46..4f4549c 100644
--- a/gd/hci/cert/cert.h
+++ b/gd/neighbor/facade/facade.h
@@ -19,15 +19,20 @@
 #include <grpc++/grpc++.h>
 
 #include "grpc/grpc_module.h"
-#include "hci/acl_manager.h"
+#include "neighbor/connectability.h"
+#include "neighbor/discoverability.h"
+#include "neighbor/inquiry.h"
+#include "neighbor/name.h"
+#include "neighbor/page.h"
+#include "neighbor/scan.h"
 
 namespace bluetooth {
-namespace hci {
-namespace cert {
+namespace neighbor {
+namespace facade {
 
-class AclManagerCertService;
+class NeighborFacadeService;
 
-class AclManagerCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class NeighborFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
@@ -37,9 +42,9 @@
   ::grpc::Service* GetService() const override;
 
  private:
-  AclManagerCertService* service_;
+  NeighborFacadeService* service_;
 };
 
-}  // namespace cert
-}  // namespace hci
+}  // namespace facade
+}  // namespace neighbor
 }  // namespace bluetooth
diff --git a/gd/neighbor/facade/facade.proto b/gd/neighbor/facade/facade.proto
new file mode 100644
index 0000000..a149547
--- /dev/null
+++ b/gd/neighbor/facade/facade.proto
@@ -0,0 +1,61 @@
+syntax = "proto3";
+
+package bluetooth.neighbor;
+
+import "google/protobuf/empty.proto";
+
+service NeighborFacade {
+  rpc SetConnectability(EnableMsg) returns (google.protobuf.Empty) {}
+  rpc SetDiscoverability(DiscoverabilitiyMsg) returns (google.protobuf.Empty) {}
+  rpc SetInquiryMode(InquiryMsg) returns (stream InquiryResultMsg) {
+    // Sets inquiry mode and fetches inquiry result HCI packet
+  }
+  rpc ReadRemoteName(RemoteNameRequestMsg) returns (google.protobuf.Empty) {}
+  rpc GetRemoteNameEvents(google.protobuf.Empty) returns (stream RemoteNameResponseMsg) {}
+  // TODO: Should we use a blocking call for ReadRemoteName instead? (Note: blocking model may not work for GD stack)
+  rpc EnableInquiryScan(EnableMsg) returns (google.protobuf.Empty) {}
+  rpc EnablePageScan(EnableMsg) returns (google.protobuf.Empty) {}
+}
+
+message EnableMsg {
+  bool enabled = 1;
+}
+
+enum DiscoverabilityMode {
+  OFF = 0;
+  LIMITED = 1;
+  GENERAL = 2;
+}
+
+message DiscoverabilitiyMsg {
+  DiscoverabilityMode mode = 1;
+}
+
+enum ResultMode {
+  STANDARD = 0;
+  RSSI = 1;
+  EXTENDED = 2;
+}
+
+message InquiryMsg {
+  DiscoverabilityMode inquiry_mode = 1;
+  ResultMode result_mode = 2;
+  uint32 length_1_28s = 3;
+  uint32 max_results = 4;  // 0 is unlimited
+}
+
+message InquiryResultMsg {
+  bytes packet = 1;
+}
+
+message RemoteNameRequestMsg {
+  bytes address = 1;
+  uint32 page_scan_repetition_mode = 2;  // r0, r1, r2
+  uint32 clock_offset = 3;
+}
+
+message RemoteNameResponseMsg {
+  uint32 status = 1;
+  bytes address = 2;
+  bytes name = 3;
+}
diff --git a/gd/neighbor/inquiry.cc b/gd/neighbor/inquiry.cc
index 4246cb2..210aefb 100644
--- a/gd/neighbor/inquiry.cc
+++ b/gd/neighbor/inquiry.cc
@@ -39,8 +39,12 @@
   void StartOneShotInquiry(bool limited, InquiryLength inquiry_length, NumResponses num_responses);
   void StopOneShotInquiry();
 
-  void StartPeriodicInquiry(bool limited, InquiryLength inquiry_length, NumResponses num_responses,
-                            PeriodLength max_delay, PeriodLength min_delay);
+  void StartPeriodicInquiry(
+      bool limited,
+      InquiryLength inquiry_length,
+      NumResponses num_responses,
+      PeriodLength max_delay,
+      PeriodLength min_delay);
   void StopPeriodicInquiry();
 
   void SetScanActivity(ScanParameters params);
@@ -204,30 +208,27 @@
     case hci::EventCode::INQUIRY_RESULT: {
       auto packet = hci::InquiryResultView::Create(view);
       ASSERT(packet.IsValid());
-      LOG_DEBUG("Inquiry result size:%zd num_responses:%d addr:%s repetition_mode:%s cod:%s clock_offset:%d",
-                packet.size(), packet.GetNumResponses(), packet.GetBdAddr().ToString().c_str(),
-                hci::PageScanRepetitionModeText(packet.GetPageScanRepetitionMode()).c_str(),
-                packet.GetClassOfDevice().ToString().c_str(), packet.GetClockOffset());
+      LOG_DEBUG("Inquiry result size:%zd num_responses:%zu", packet.size(), packet.GetInquiryResults().size());
       inquiry_callbacks_.result(packet);
     } break;
 
     case hci::EventCode::INQUIRY_RESULT_WITH_RSSI: {
       auto packet = hci::InquiryResultWithRssiView::Create(view);
       ASSERT(packet.IsValid());
-      LOG_DEBUG("Inquiry result with rssi num_responses:%d addr:%s repetition_mode:%s cod:%s clock_offset:%d",
-                packet.GetNumResponses(), packet.GetAddress().ToString().c_str(),
-                hci::PageScanRepetitionModeText(packet.GetPageScanRepetitionMode()).c_str(),
-                packet.GetClassOfDevice().ToString().c_str(), packet.GetClockOffset());
+      LOG_DEBUG("Inquiry result with rssi num_responses:%zu", packet.GetInquiryResults().size());
       inquiry_callbacks_.result_with_rssi(packet);
     } break;
 
     case hci::EventCode::EXTENDED_INQUIRY_RESULT: {
       auto packet = hci::ExtendedInquiryResultView::Create(view);
       ASSERT(packet.IsValid());
-      LOG_DEBUG("Extended inquiry result addr:%s repetition_mode:%s cod:%s clock_offset:%d rssi:%hhd",
-                packet.GetAddress().ToString().c_str(),
-                hci::PageScanRepetitionModeText(packet.GetPageScanRepetitionMode()).c_str(),
-                packet.GetClassOfDevice().ToString().c_str(), packet.GetClockOffset(), packet.GetRssi());
+      LOG_DEBUG(
+          "Extended inquiry result addr:%s repetition_mode:%s cod:%s clock_offset:%d rssi:%hhd",
+          packet.GetAddress().ToString().c_str(),
+          hci::PageScanRepetitionModeText(packet.GetPageScanRepetitionMode()).c_str(),
+          packet.GetClassOfDevice().ToString().c_str(),
+          packet.GetClockOffset(),
+          packet.GetRssi());
       inquiry_callbacks_.extended_result(packet);
     } break;
 
@@ -243,14 +244,14 @@
 void neighbor::InquiryModule::impl::RegisterCallbacks(InquiryCallbacks callbacks) {
   inquiry_callbacks_ = callbacks;
 
-  hci_layer_->RegisterEventHandler(hci::EventCode::INQUIRY_RESULT,
-                                   common::Bind(&InquiryModule::impl::OnEvent, common::Unretained(this)), handler_);
-  hci_layer_->RegisterEventHandler(hci::EventCode::INQUIRY_RESULT_WITH_RSSI,
-                                   common::Bind(&InquiryModule::impl::OnEvent, common::Unretained(this)), handler_);
-  hci_layer_->RegisterEventHandler(hci::EventCode::EXTENDED_INQUIRY_RESULT,
-                                   common::Bind(&InquiryModule::impl::OnEvent, common::Unretained(this)), handler_);
-  hci_layer_->RegisterEventHandler(hci::EventCode::INQUIRY_COMPLETE,
-                                   common::Bind(&InquiryModule::impl::OnEvent, common::Unretained(this)), handler_);
+  hci_layer_->RegisterEventHandler(
+      hci::EventCode::INQUIRY_RESULT, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
+  hci_layer_->RegisterEventHandler(
+      hci::EventCode::INQUIRY_RESULT_WITH_RSSI, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
+  hci_layer_->RegisterEventHandler(
+      hci::EventCode::EXTENDED_INQUIRY_RESULT, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
+  hci_layer_->RegisterEventHandler(
+      hci::EventCode::INQUIRY_COMPLETE, handler_->BindOn(this, &InquiryModule::impl::OnEvent));
 }
 
 void neighbor::InquiryModule::impl::UnregisterCallbacks() {
@@ -263,28 +264,25 @@
 }
 
 void neighbor::InquiryModule::impl::EnqueueCommandComplete(std::unique_ptr<hci::CommandPacketBuilder> command) {
-  hci_layer_->EnqueueCommand(std::move(command), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
-                             handler_);
+  hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandComplete));
 }
 
 void neighbor::InquiryModule::impl::EnqueueCommandStatus(std::unique_ptr<hci::CommandPacketBuilder> command) {
-  hci_layer_->EnqueueCommand(std::move(command), common::BindOnce(&impl::OnCommandStatus, common::Unretained(this)),
-                             handler_);
+  hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandStatus));
 }
 
 void neighbor::InquiryModule::impl::EnqueueCommandCompleteSync(std::unique_ptr<hci::CommandPacketBuilder> command) {
   ASSERT(command_sync_ == nullptr);
   command_sync_ = new std::promise<void>();
   auto command_received = command_sync_->get_future();
-  hci_layer_->EnqueueCommand(std::move(command),
-                             common::BindOnce(&impl::OnCommandCompleteSync, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandCompleteSync));
   command_received.wait();
   delete command_sync_;
   command_sync_ = nullptr;
 }
 
-void neighbor::InquiryModule::impl::StartOneShotInquiry(bool limited, InquiryLength inquiry_length,
-                                                        NumResponses num_responses) {
+void neighbor::InquiryModule::impl::StartOneShotInquiry(
+    bool limited, InquiryLength inquiry_length, NumResponses num_responses) {
   ASSERT(HasCallbacks());
   ASSERT(!IsInquiryActive());
   hci::Lap lap;
@@ -305,9 +303,12 @@
   EnqueueCommandComplete(hci::InquiryCancelBuilder::Create());
 }
 
-void neighbor::InquiryModule::impl::StartPeriodicInquiry(bool limited, InquiryLength inquiry_length,
-                                                         NumResponses num_responses, PeriodLength max_delay,
-                                                         PeriodLength min_delay) {
+void neighbor::InquiryModule::impl::StartPeriodicInquiry(
+    bool limited,
+    InquiryLength inquiry_length,
+    NumResponses num_responses,
+    PeriodLength max_delay,
+    PeriodLength min_delay) {
   ASSERT(HasCallbacks());
   ASSERT(!IsInquiryActive());
   hci::Lap lap;
@@ -347,8 +348,10 @@
 
 void neighbor::InquiryModule::impl::Stop() {
   LOG_INFO("Inquiry scan interval:%hu window:%hu", inquiry_scan_.interval, inquiry_scan_.window);
-  LOG_INFO("Inquiry mode:%s scan_type:%s", hci::InquiryModeText(inquiry_mode_).c_str(),
-           hci::InquiryScanTypeText(inquiry_scan_type_).c_str());
+  LOG_INFO(
+      "Inquiry mode:%s scan_type:%s",
+      hci::InquiryModeText(inquiry_mode_).c_str(),
+      hci::InquiryScanTypeText(inquiry_scan_type_).c_str());
   LOG_INFO("Inquiry response tx power:%hhd", inquiry_response_tx_power_);
   LOG_DEBUG("Stopped inquiry module");
 }
@@ -362,8 +365,12 @@
 void neighbor::InquiryModule::impl::SetScanActivity(ScanParameters params) {
   EnqueueCommandComplete(hci::WriteInquiryScanActivityBuilder::Create(params.interval, params.window));
   inquiry_scan_ = params;
-  LOG_DEBUG("Set scan activity interval:0x%x/%.02fms window:0x%x/%.02fms", params.interval,
-            ScanIntervalTimeMs(params.interval), params.window, ScanWindowTimeMs(params.window));
+  LOG_DEBUG(
+      "Set scan activity interval:0x%x/%.02fms window:0x%x/%.02fms",
+      params.interval,
+      ScanIntervalTimeMs(params.interval),
+      params.window,
+      ScanWindowTimeMs(params.window));
 }
 
 void neighbor::InquiryModule::impl::SetScanType(hci::InquiryScanType scan_type) {
@@ -394,13 +401,21 @@
 }
 
 void neighbor::InquiryModule::StartGeneralInquiry(InquiryLength inquiry_length, NumResponses num_responses) {
-  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::StartOneShotInquiry,
-                                      common::Unretained(pimpl_.get()), false, inquiry_length, num_responses));
+  GetHandler()->Post(common::BindOnce(
+      &neighbor::InquiryModule::impl::StartOneShotInquiry,
+      common::Unretained(pimpl_.get()),
+      false,
+      inquiry_length,
+      num_responses));
 }
 
 void neighbor::InquiryModule::StartLimitedInquiry(InquiryLength inquiry_length, NumResponses num_responses) {
-  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::StartOneShotInquiry,
-                                      common::Unretained(pimpl_.get()), true, inquiry_length, num_responses));
+  GetHandler()->Post(common::BindOnce(
+      &neighbor::InquiryModule::impl::StartOneShotInquiry,
+      common::Unretained(pimpl_.get()),
+      true,
+      inquiry_length,
+      num_responses));
 }
 
 void neighbor::InquiryModule::StopInquiry() {
@@ -408,18 +423,28 @@
       common::BindOnce(&neighbor::InquiryModule::impl::StopOneShotInquiry, common::Unretained(pimpl_.get())));
 }
 
-void neighbor::InquiryModule::StartGeneralPeriodicInquiry(InquiryLength inquiry_length, NumResponses num_responses,
-                                                          PeriodLength max_delay, PeriodLength min_delay) {
-  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::StartPeriodicInquiry,
-                                      common::Unretained(pimpl_.get()), false, inquiry_length, num_responses, max_delay,
-                                      min_delay));
+void neighbor::InquiryModule::StartGeneralPeriodicInquiry(
+    InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay, PeriodLength min_delay) {
+  GetHandler()->Post(common::BindOnce(
+      &neighbor::InquiryModule::impl::StartPeriodicInquiry,
+      common::Unretained(pimpl_.get()),
+      false,
+      inquiry_length,
+      num_responses,
+      max_delay,
+      min_delay));
 }
 
-void neighbor::InquiryModule::StartLimitedPeriodicInquiry(InquiryLength inquiry_length, NumResponses num_responses,
-                                                          PeriodLength max_delay, PeriodLength min_delay) {
-  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::StartPeriodicInquiry,
-                                      common::Unretained(pimpl_.get()), true, inquiry_length, num_responses, max_delay,
-                                      min_delay));
+void neighbor::InquiryModule::StartLimitedPeriodicInquiry(
+    InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay, PeriodLength min_delay) {
+  GetHandler()->Post(common::BindOnce(
+      &neighbor::InquiryModule::impl::StartPeriodicInquiry,
+      common::Unretained(pimpl_.get()),
+      true,
+      inquiry_length,
+      num_responses,
+      max_delay,
+      min_delay));
 }
 
 void neighbor::InquiryModule::StopPeriodicInquiry() {
@@ -433,28 +458,30 @@
 }
 
 void neighbor::InquiryModule::SetInterlacedScan() {
-  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()),
-                                      hci::InquiryScanType::INTERLACED));
+  GetHandler()->Post(common::BindOnce(
+      &neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()), hci::InquiryScanType::INTERLACED));
 }
 
 void neighbor::InquiryModule::SetStandardScan() {
-  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()),
-                                      hci::InquiryScanType::STANDARD));
+  GetHandler()->Post(common::BindOnce(
+      &neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()), hci::InquiryScanType::STANDARD));
 }
 
 void neighbor::InquiryModule::SetStandardInquiryResultMode() {
-  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()),
-                                      hci::InquiryMode::STANDARD));
+  GetHandler()->Post(common::BindOnce(
+      &neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()), hci::InquiryMode::STANDARD));
 }
 
 void neighbor::InquiryModule::SetInquiryWithRssiResultMode() {
-  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()),
-                                      hci::InquiryMode::RSSI));
+  GetHandler()->Post(common::BindOnce(
+      &neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()), hci::InquiryMode::RSSI));
 }
 
 void neighbor::InquiryModule::SetExtendedInquiryResultMode() {
-  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()),
-                                      hci::InquiryMode::RSSI_OR_EXTENDED));
+  GetHandler()->Post(common::BindOnce(
+      &neighbor::InquiryModule::impl::SetInquiryMode,
+      common::Unretained(pimpl_.get()),
+      hci::InquiryMode::RSSI_OR_EXTENDED));
 }
 
 /**
diff --git a/gd/neighbor/inquiry.h b/gd/neighbor/inquiry.h
index 33e3d6c..6896fc2 100644
--- a/gd/neighbor/inquiry.h
+++ b/gd/neighbor/inquiry.h
@@ -49,10 +49,10 @@
   void StartLimitedInquiry(InquiryLength inquiry_length, NumResponses num_responses);
   void StopInquiry();
 
-  void StartGeneralPeriodicInquiry(InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay,
-                                   PeriodLength min_delay);
-  void StartLimitedPeriodicInquiry(InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay,
-                                   PeriodLength min_delay);
+  void StartGeneralPeriodicInquiry(
+      InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay, PeriodLength min_delay);
+  void StartLimitedPeriodicInquiry(
+      InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay, PeriodLength min_delay);
   void StopPeriodicInquiry();
 
   void SetScanActivity(ScanParameters parms);
diff --git a/gd/neighbor/inquiry_test.cc b/gd/neighbor/inquiry_test.cc
index 936a897..e81486b 100644
--- a/gd/neighbor/inquiry_test.cc
+++ b/gd/neighbor/inquiry_test.cc
@@ -16,16 +16,15 @@
 
 #include "neighbor/inquiry.h"
 
+#include <gtest/gtest.h>
+#include <unistd.h>
+
 #include <algorithm>
 #include <chrono>
 #include <future>
 #include <map>
 #include <memory>
 
-#include <unistd.h>
-
-#include <gtest/gtest.h>
-
 #include "common/bind.h"
 #include "common/callback.h"
 #include "hci/address.h"
@@ -85,22 +84,25 @@
 
 class TestHciLayer : public hci::HciLayer {
  public:
-  void EnqueueCommand(std::unique_ptr<hci::CommandPacketBuilder> command,
-                      common::OnceCallback<void(hci::CommandCompleteView)> on_complete, os::Handler* handler) override {
-    GetHandler()->Post(common::BindOnce(&TestHciLayer::HandleCommand, common::Unretained(this), std::move(command),
-                                        std::move(on_complete), common::Unretained(handler)));
+  void EnqueueCommand(
+      std::unique_ptr<hci::CommandPacketBuilder> command,
+      common::ContextualOnceCallback<void(hci::CommandCompleteView)> on_complete) override {
+    GetHandler()->Post(common::BindOnce(
+        &TestHciLayer::HandleCommand, common::Unretained(this), std::move(command), std::move(on_complete)));
   }
 
-  void EnqueueCommand(std::unique_ptr<hci::CommandPacketBuilder> command,
-                      common::OnceCallback<void(hci::CommandStatusView)> on_status, os::Handler* handler) override {
-    GetHandler()->Post(common::BindOnce(&TestHciLayer::HandleStatus, common::Unretained(this), std::move(command),
-                                        std::move(on_status), common::Unretained(handler)));
+  void EnqueueCommand(
+      std::unique_ptr<hci::CommandPacketBuilder> command,
+      common::ContextualOnceCallback<void(hci::CommandStatusView)> on_status) override {
+    GetHandler()->Post(common::BindOnce(
+        &TestHciLayer::HandleStatus, common::Unretained(this), std::move(command), std::move(on_status)));
   }
 
-  void HandleCommand(std::unique_ptr<hci::CommandPacketBuilder> command_builder,
-                     common::OnceCallback<void(hci::CommandCompleteView)> on_complete, os::Handler* handler) {
+  void HandleCommand(
+      std::unique_ptr<hci::CommandPacketBuilder> command_builder,
+      common::ContextualOnceCallback<void(hci::CommandCompleteView)> on_complete) {
     hci::CommandPacketView command = hci::CommandPacketView::Create(GetPacketView(std::move(command_builder)));
-    ASSERT(command.IsValid());
+    ASSERT_TRUE(command.IsValid());
 
     std::unique_ptr<packet::BasePacketBuilder> event_builder;
     switch (command.GetOpCode()) {
@@ -112,7 +114,7 @@
 
       case hci::OpCode::PERIODIC_INQUIRY_MODE: {
         auto inquiry = hci::PeriodicInquiryModeView::Create(hci::DiscoveryCommandView::Create(command));
-        ASSERT(inquiry.IsValid());
+        ASSERT_TRUE(inquiry.IsValid());
         event_builder =
             hci::PeriodicInquiryModeCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
         hci_register_.periodic_inquiry_active = true;
@@ -133,7 +135,7 @@
             hci::WriteInquiryModeCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
         {
           auto view = hci::WriteInquiryModeView::Create(hci::DiscoveryCommandView::Create(command));
-          ASSERT(view.IsValid());
+          ASSERT_TRUE(view.IsValid());
           hci_register_.inquiry_mode = view.GetInquiryMode();
         }
         break;
@@ -148,7 +150,7 @@
             hci::WriteInquiryScanActivityCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
         {
           auto view = hci::WriteInquiryScanActivityView::Create(hci::DiscoveryCommandView::Create(command));
-          ASSERT(view.IsValid());
+          ASSERT_TRUE(view.IsValid());
           hci_register_.inquiry_scan_interval = view.GetInquiryScanInterval();
           hci_register_.inquiry_scan_window = view.GetInquiryScanWindow();
         }
@@ -156,7 +158,9 @@
 
       case hci::OpCode::READ_INQUIRY_SCAN_ACTIVITY:
         event_builder = hci::ReadInquiryScanActivityCompleteBuilder::Create(
-            kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS, hci_register_.inquiry_scan_interval,
+            kNumberPacketsReadyToReceive,
+            hci::ErrorCode::SUCCESS,
+            hci_register_.inquiry_scan_interval,
             hci_register_.inquiry_scan_window);
         break;
 
@@ -165,7 +169,7 @@
             hci::WriteInquiryScanTypeCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
         {
           auto view = hci::WriteInquiryScanTypeView::Create(hci::DiscoveryCommandView::Create(command));
-          ASSERT(view.IsValid());
+          ASSERT_TRUE(view.IsValid());
           hci_register_.inquiry_scan_type = view.GetInquiryScanType();
         }
         break;
@@ -185,26 +189,27 @@
         return;
     }
     hci::EventPacketView event = hci::EventPacketView::Create(GetPacketView(std::move(event_builder)));
-    ASSERT(event.IsValid());
+    ASSERT_TRUE(event.IsValid());
     hci::CommandCompleteView command_complete = hci::CommandCompleteView::Create(event);
-    ASSERT(command_complete.IsValid());
-    handler->Post(common::BindOnce(std::move(on_complete), std::move(command_complete)));
+    ASSERT_TRUE(command_complete.IsValid());
+    on_complete.Invoke(std::move(command_complete));
 
     if (promise_sync_complete_ != nullptr) {
       promise_sync_complete_->set_value(command.GetOpCode());
     }
   }
 
-  void HandleStatus(std::unique_ptr<hci::CommandPacketBuilder> command_builder,
-                    common::OnceCallback<void(hci::CommandStatusView)> on_status, os::Handler* handler) {
+  void HandleStatus(
+      std::unique_ptr<hci::CommandPacketBuilder> command_builder,
+      common::ContextualOnceCallback<void(hci::CommandStatusView)> on_status) {
     hci::CommandPacketView command = hci::CommandPacketView::Create(GetPacketView(std::move(command_builder)));
-    ASSERT(command.IsValid());
+    ASSERT_TRUE(command.IsValid());
 
     std::unique_ptr<packet::BasePacketBuilder> event_builder;
     switch (command.GetOpCode()) {
       case hci::OpCode::INQUIRY: {
         auto inquiry = hci::InquiryView::Create(hci::DiscoveryCommandView::Create(command));
-        ASSERT(inquiry.IsValid());
+        ASSERT_TRUE(inquiry.IsValid());
         event_builder = hci::InquiryStatusBuilder::Create(hci::ErrorCode::SUCCESS, kNumberPacketsReadyToReceive);
         hci_register_.one_shot_inquiry_active = true;
         hci_register_.num_responses = inquiry.GetNumResponses();
@@ -215,33 +220,29 @@
         return;
     }
     hci::EventPacketView event = hci::EventPacketView::Create(GetPacketView(std::move(event_builder)));
-    ASSERT(event.IsValid());
+    ASSERT_TRUE(event.IsValid());
     hci::CommandStatusView command_status = hci::CommandStatusView::Create(event);
-    ASSERT(command_status.IsValid());
-    handler->Post(common::BindOnce(std::move(on_status), std::move(command_status)));
+    ASSERT_TRUE(command_status.IsValid());
+    on_status.Invoke(std::move(command_status));
 
     if (promise_sync_complete_ != nullptr) {
       promise_sync_complete_->set_value(command.GetOpCode());
     }
   }
 
-  void RegisterEventHandler(hci::EventCode event_code, common::Callback<void(hci::EventPacketView)> event_handler,
-                            os::Handler* handler) override {
+  void RegisterEventHandler(
+      hci::EventCode event_code, common::ContextualCallback<void(hci::EventPacketView)> event_handler) override {
     switch (event_code) {
       case hci::EventCode::INQUIRY_RESULT:
-        inquiry_result_handler_ = handler;
         inquiry_result_callback_ = event_handler;
         break;
       case hci::EventCode::INQUIRY_RESULT_WITH_RSSI:
-        inquiry_result_with_rssi_handler_ = handler;
         inquiry_result_with_rssi_callback_ = event_handler;
         break;
       case hci::EventCode::EXTENDED_INQUIRY_RESULT:
-        extended_inquiry_result_handler_ = handler;
         extended_inquiry_result_callback_ = event_handler;
         break;
       case hci::EventCode::INQUIRY_COMPLETE:
-        inquiry_complete_handler_ = handler;
         inquiry_complete_callback_ = event_handler;
         break;
       default:
@@ -258,19 +259,15 @@
 
     switch (event_code) {
       case hci::EventCode::INQUIRY_RESULT:
-        inquiry_result_handler_ = nullptr;
         inquiry_result_callback_ = {};
         break;
       case hci::EventCode::INQUIRY_RESULT_WITH_RSSI:
-        inquiry_result_with_rssi_handler_ = nullptr;
         inquiry_result_with_rssi_callback_ = {};
         break;
       case hci::EventCode::EXTENDED_INQUIRY_RESULT:
-        extended_inquiry_result_handler_ = nullptr;
         extended_inquiry_result_callback_ = {};
         break;
       case hci::EventCode::INQUIRY_COMPLETE:
-        inquiry_complete_handler_ = nullptr;
         inquiry_complete_callback_ = {};
         break;
       default:
@@ -280,7 +277,7 @@
   }
 
   void Synchronize(std::function<void()> func, hci::OpCode op_code) {
-    ASSERT(promise_sync_complete_ == nullptr);
+    ASSERT_EQ(promise_sync_complete_, nullptr);
     promise_sync_complete_ = new std::promise<hci::OpCode>();
     auto future = promise_sync_complete_->get_future();
     func();
@@ -292,11 +289,9 @@
   }
 
   void InjectInquiryResult(std::unique_ptr<hci::InquiryResultBuilder> result) {
-    if (inquiry_result_handler_ != nullptr) {
-      hci::EventPacketView view = hci::EventPacketView::Create(GetPacketView(std::move(result)));
-      ASSERT(view.IsValid());
-      inquiry_result_handler_->Post(common::BindOnce(inquiry_result_callback_, std::move(view)));
-    }
+    hci::EventPacketView view = hci::EventPacketView::Create(GetPacketView(std::move(result)));
+    ASSERT_TRUE(view.IsValid());
+    inquiry_result_callback_.Invoke(std::move(view));
   }
 
   void ListDependencies(ModuleList* list) override {}
@@ -306,25 +301,21 @@
  private:
   std::promise<hci::OpCode>* promise_sync_complete_{nullptr};
 
-  os::Handler* inquiry_result_handler_{nullptr};
-  common::Callback<void(hci::EventPacketView)> inquiry_result_callback_;
-  os::Handler* inquiry_result_with_rssi_handler_{nullptr};
-  common::Callback<void(hci::EventPacketView)> inquiry_result_with_rssi_callback_;
-  os::Handler* extended_inquiry_result_handler_{nullptr};
-  common::Callback<void(hci::EventPacketView)> extended_inquiry_result_callback_;
-  os::Handler* inquiry_complete_handler_{nullptr};
-  common::Callback<void(hci::EventPacketView)> inquiry_complete_callback_;
+  common::ContextualCallback<void(hci::EventPacketView)> inquiry_result_callback_;
+  common::ContextualCallback<void(hci::EventPacketView)> inquiry_result_with_rssi_callback_;
+  common::ContextualCallback<void(hci::EventPacketView)> extended_inquiry_result_callback_;
+  common::ContextualCallback<void(hci::EventPacketView)> inquiry_complete_callback_;
 };
 
 class InquiryTest : public ::testing::Test {
  public:
   void Result(hci::InquiryResultView view) {
-    ASSERT(view.size() >= sizeof(uint16_t));
+    ASSERT_TRUE(view.size() >= sizeof(uint16_t));
     promise_result_complete_->set_value(true);
   }
 
   void WaitForInquiryResult(std::function<void()> func) {
-    ASSERT(promise_result_complete_ == nullptr);
+    ASSERT_EQ(promise_result_complete_, nullptr);
     promise_result_complete_ = new std::promise<bool>();
     auto future = promise_result_complete_->get_future();
     func();
@@ -334,11 +325,11 @@
   }
 
   void ResultWithRssi(hci::InquiryResultWithRssiView view) {
-    ASSERT(view.size() >= sizeof(uint16_t));
+    ASSERT_TRUE(view.size() >= sizeof(uint16_t));
   }
 
   void ExtendedResult(hci::ExtendedInquiryResultView view) {
-    ASSERT(view.size() >= sizeof(uint16_t));
+    ASSERT_TRUE(view.size() >= sizeof(uint16_t));
   }
 
   void Complete(hci::ErrorCode status) {}
@@ -377,16 +368,16 @@
 TEST_F(InquiryTest, Module) {}
 
 TEST_F(InquiryTest, SetInquiryModes) {
-  test_hci_layer_->Synchronize([this] { inquiry_module_->SetInquiryWithRssiResultMode(); },
-                               hci::OpCode::WRITE_INQUIRY_MODE);
+  test_hci_layer_->Synchronize(
+      [this] { inquiry_module_->SetInquiryWithRssiResultMode(); }, hci::OpCode::WRITE_INQUIRY_MODE);
   ASSERT_EQ(hci_register_.inquiry_mode, hci::InquiryMode::RSSI);
 
-  test_hci_layer_->Synchronize([this] { inquiry_module_->SetExtendedInquiryResultMode(); },
-                               hci::OpCode::WRITE_INQUIRY_MODE);
+  test_hci_layer_->Synchronize(
+      [this] { inquiry_module_->SetExtendedInquiryResultMode(); }, hci::OpCode::WRITE_INQUIRY_MODE);
   ASSERT_EQ(hci_register_.inquiry_mode, hci::InquiryMode::RSSI_OR_EXTENDED);
 
-  test_hci_layer_->Synchronize([this] { inquiry_module_->SetStandardInquiryResultMode(); },
-                               hci::OpCode::WRITE_INQUIRY_MODE);
+  test_hci_layer_->Synchronize(
+      [this] { inquiry_module_->SetStandardInquiryResultMode(); }, hci::OpCode::WRITE_INQUIRY_MODE);
   ASSERT_EQ(hci_register_.inquiry_mode, hci::InquiryMode::STANDARD);
 }
 
@@ -404,8 +395,8 @@
       .window = 0x5678,
   };
 
-  test_hci_layer_->Synchronize([this, params] { inquiry_module_->SetScanActivity(params); },
-                               hci::OpCode::WRITE_INQUIRY_SCAN_ACTIVITY);
+  test_hci_layer_->Synchronize(
+      [this, params] { inquiry_module_->SetScanActivity(params); }, hci::OpCode::WRITE_INQUIRY_SCAN_ACTIVITY);
   ASSERT_EQ(params.interval, hci_register_.inquiry_scan_interval);
   ASSERT_EQ(params.window, hci_register_.inquiry_scan_window);
 }
@@ -443,31 +434,25 @@
   ASSERT_EQ(max_delay, hci_register_.max_period_length);
   ASSERT_EQ(min_delay, hci_register_.min_period_length);
 
-  test_hci_layer_->Synchronize([this] { inquiry_module_->StopPeriodicInquiry(); },
-                               hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE);
+  test_hci_layer_->Synchronize(
+      [this] { inquiry_module_->StopPeriodicInquiry(); }, hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE);
 }
 
 TEST_F(InquiryTest, LimitedPeriodicInquiry) {
-  test_hci_layer_->Synchronize([this] { inquiry_module_->StartLimitedPeriodicInquiry(128, 100, 1100, 200); },
-                               hci::OpCode::PERIODIC_INQUIRY_MODE);
+  test_hci_layer_->Synchronize(
+      [this] { inquiry_module_->StartLimitedPeriodicInquiry(128, 100, 1100, 200); },
+      hci::OpCode::PERIODIC_INQUIRY_MODE);
 
-  test_hci_layer_->Synchronize([this] { inquiry_module_->StopPeriodicInquiry(); },
-                               hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE);
+  test_hci_layer_->Synchronize(
+      [this] { inquiry_module_->StopPeriodicInquiry(); }, hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE);
 }
 
 TEST_F(InquiryTest, InjectInquiryResult) {
   test_hci_layer_->Synchronize([this] { inquiry_module_->StartGeneralInquiry(128, 100); }, hci::OpCode::INQUIRY);
 
   WaitForInquiryResult([this] {
-    uint8_t num_responses = 1;
-    hci::Address bd_addr;
-    hci::Address::FromString("11:22:33:44:55:66", bd_addr);
-    hci::PageScanRepetitionMode page_scan_repetition_mode = hci::PageScanRepetitionMode::R1;
-    hci::ClassOfDevice class_of_device;
-    hci::ClassOfDevice::FromString("00:00:00", class_of_device);
-    uint16_t clock_offset = 0x1234;
-    auto packet = hci::InquiryResultBuilder::Create(num_responses, bd_addr, page_scan_repetition_mode, class_of_device,
-                                                    clock_offset);
+    const std::vector<hci::InquiryResult> inquiry_results;
+    auto packet = hci::InquiryResultBuilder::Create(inquiry_results);
     test_hci_layer_->InjectInquiryResult(std::move(packet));
   });
   test_hci_layer_->Synchronize([this] { inquiry_module_->StopInquiry(); }, hci::OpCode::INQUIRY_CANCEL);
diff --git a/gd/neighbor/name.cc b/gd/neighbor/name.cc
index 34fa86b..ec28e5a 100644
--- a/gd/neighbor/name.cc
+++ b/gd/neighbor/name.cc
@@ -41,12 +41,16 @@
   os::Handler* handler;
 };
 
-constexpr std::array<uint8_t, 248> kEmptyName{};
+constexpr RemoteName kEmptyName{};
 
 struct NameModule::impl {
-  void ReadRemoteNameRequest(hci::Address address, hci::PageScanRepetitionMode page_scan_repetition_mode,
-                             uint16_t clock_offset, hci::ClockOffsetValid clock_offset_valid,
-                             ReadRemoteNameCallback callback, os::Handler* handler);
+  void ReadRemoteNameRequest(
+      hci::Address address,
+      hci::PageScanRepetitionMode page_scan_repetition_mode,
+      uint16_t clock_offset,
+      hci::ClockOffsetValid clock_offset_valid,
+      ReadRemoteNameCallback callback,
+      os::Handler* handler);
   void CancelRemoteNameRequest(hci::Address address, CancelRemoteNameCallback, os::Handler* handler);
 
   void Start();
@@ -76,13 +80,11 @@
 neighbor::NameModule::impl::impl(const neighbor::NameModule& module) : module_(module) {}
 
 void neighbor::NameModule::impl::EnqueueCommandComplete(std::unique_ptr<hci::CommandPacketBuilder> command) {
-  hci_layer_->EnqueueCommand(std::move(command), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
-                             handler_);
+  hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandComplete));
 }
 
 void neighbor::NameModule::impl::EnqueueCommandStatus(std::unique_ptr<hci::CommandPacketBuilder> command) {
-  hci_layer_->EnqueueCommand(std::move(command), common::BindOnce(&impl::OnCommandStatus, common::Unretained(this)),
-                             handler_);
+  hci_layer_->EnqueueCommand(std::move(command), handler_->BindOnceOn(this, &impl::OnCommandStatus));
 }
 
 void neighbor::NameModule::impl::OnCommandComplete(hci::CommandCompleteView view) {
@@ -124,8 +126,8 @@
       hci::Address address = packet.GetBdAddr();
       ASSERT(read_callback_handler_map_.find(address) != read_callback_handler_map_.end());
       auto read_callback_handler = std::move(read_callback_handler_map_[address]);
-      read_callback_handler->handler->Post(common::BindOnce(std::move(read_callback_handler->callback),
-                                                            packet.GetStatus(), address, packet.GetRemoteName()));
+      read_callback_handler->handler->Post(common::BindOnce(
+          std::move(read_callback_handler->callback), packet.GetStatus(), address, packet.GetRemoteName()));
       read_callback_handler_map_.erase(address);
     } break;
     default:
@@ -138,18 +140,21 @@
   hci_layer_ = module_.GetDependency<hci::HciLayer>();
   handler_ = module_.GetHandler();
 
-  hci_layer_->RegisterEventHandler(hci::EventCode::REMOTE_NAME_REQUEST_COMPLETE,
-                                   common::Bind(&NameModule::impl::OnEvent, common::Unretained(this)), handler_);
+  hci_layer_->RegisterEventHandler(
+      hci::EventCode::REMOTE_NAME_REQUEST_COMPLETE, handler_->BindOn(this, &NameModule::impl::OnEvent));
 }
 
 void neighbor::NameModule::impl::Stop() {
   hci_layer_->UnregisterEventHandler(hci::EventCode::REMOTE_NAME_REQUEST_COMPLETE);
 }
 
-void neighbor::NameModule::impl::ReadRemoteNameRequest(hci::Address address,
-                                                       hci::PageScanRepetitionMode page_scan_repetition_mode,
-                                                       uint16_t clock_offset, hci::ClockOffsetValid clock_offset_valid,
-                                                       ReadRemoteNameCallback callback, os::Handler* handler) {
+void neighbor::NameModule::impl::ReadRemoteNameRequest(
+    hci::Address address,
+    hci::PageScanRepetitionMode page_scan_repetition_mode,
+    uint16_t clock_offset,
+    hci::ClockOffsetValid clock_offset_valid,
+    ReadRemoteNameCallback callback,
+    os::Handler* handler) {
   LOG_DEBUG("%s Start read remote name request for %s", __func__, address.ToString().c_str());
 
   if (read_callback_handler_map_.find(address) != read_callback_handler_map_.end()) {
@@ -166,8 +171,8 @@
       hci::RemoteNameRequestBuilder::Create(address, page_scan_repetition_mode, clock_offset, clock_offset_valid));
 }
 
-void neighbor::NameModule::impl::CancelRemoteNameRequest(hci::Address address, CancelRemoteNameCallback callback,
-                                                         os::Handler* handler) {
+void neighbor::NameModule::impl::CancelRemoteNameRequest(
+    hci::Address address, CancelRemoteNameCallback callback, os::Handler* handler) {
   LOG_DEBUG("%s Cancel remote name request for %s", __func__, address.ToString().c_str());
 
   if (cancel_callback_handler_map_.find(address) != cancel_callback_handler_map_.end()) {
@@ -191,19 +196,36 @@
   pimpl_.reset();
 }
 
-void neighbor::NameModule::ReadRemoteNameRequest(hci::Address address,
-                                                 hci::PageScanRepetitionMode page_scan_repetition_mode,
-                                                 uint16_t clock_offset, hci::ClockOffsetValid clock_offset_valid,
-                                                 ReadRemoteNameCallback callback, os::Handler* handler) {
-  GetHandler()->Post(common::BindOnce(&NameModule::impl::ReadRemoteNameRequest, common::Unretained(pimpl_.get()),
-                                      address, page_scan_repetition_mode, clock_offset, clock_offset_valid,
-                                      std::move(callback), handler));
+void neighbor::NameModule::ReadRemoteNameRequest(
+    hci::Address address,
+    hci::PageScanRepetitionMode page_scan_repetition_mode,
+    uint16_t clock_offset,
+    hci::ClockOffsetValid clock_offset_valid,
+    ReadRemoteNameCallback callback,
+    os::Handler* handler) {
+  ASSERT(callback);
+  ASSERT(handler != nullptr);
+  GetHandler()->Post(common::BindOnce(
+      &NameModule::impl::ReadRemoteNameRequest,
+      common::Unretained(pimpl_.get()),
+      address,
+      page_scan_repetition_mode,
+      clock_offset,
+      clock_offset_valid,
+      std::move(callback),
+      handler));
 }
 
-void neighbor::NameModule::CancelRemoteNameRequest(hci::Address address, CancelRemoteNameCallback callback,
-                                                   os::Handler* handler) {
-  GetHandler()->Post(common::BindOnce(&NameModule::impl::CancelRemoteNameRequest, common::Unretained(pimpl_.get()),
-                                      address, std::move(callback), handler));
+void neighbor::NameModule::CancelRemoteNameRequest(
+    hci::Address address, CancelRemoteNameCallback callback, os::Handler* handler) {
+  ASSERT(callback);
+  ASSERT(handler != nullptr);
+  GetHandler()->Post(common::BindOnce(
+      &NameModule::impl::CancelRemoteNameRequest,
+      common::Unretained(pimpl_.get()),
+      address,
+      std::move(callback),
+      handler));
 }
 
 /**
diff --git a/gd/neighbor/name.h b/gd/neighbor/name.h
index 291e1de..779ae74 100644
--- a/gd/neighbor/name.h
+++ b/gd/neighbor/name.h
@@ -27,15 +27,19 @@
 namespace bluetooth {
 namespace neighbor {
 
-using ReadRemoteNameCallback =
-    common::OnceCallback<void(hci::ErrorCode status, hci::Address address, std::array<uint8_t, 248> name)>;
+using RemoteName = std::array<uint8_t, 248>;
+using ReadRemoteNameCallback = common::OnceCallback<void(hci::ErrorCode status, hci::Address address, RemoteName name)>;
 using CancelRemoteNameCallback = common::OnceCallback<void(hci::ErrorCode status, hci::Address address)>;
 
 class NameModule : public bluetooth::Module {
  public:
-  void ReadRemoteNameRequest(hci::Address address, hci::PageScanRepetitionMode page_scan_repetition_mode,
-                             uint16_t clock_offset, hci::ClockOffsetValid clock_offset_valid,
-                             ReadRemoteNameCallback on_read_name, os::Handler* handler);
+  void ReadRemoteNameRequest(
+      hci::Address address,
+      hci::PageScanRepetitionMode page_scan_repetition_mode,
+      uint16_t clock_offset,
+      hci::ClockOffsetValid clock_offset_valid,
+      ReadRemoteNameCallback on_read_name,
+      os::Handler* handler);
   void CancelRemoteNameRequest(hci::Address address, CancelRemoteNameCallback on_cancel, os::Handler* handler);
 
   static const ModuleFactory Factory;
diff --git a/gd/neighbor/name_db.cc b/gd/neighbor/name_db.cc
new file mode 100644
index 0000000..8cd2839
--- /dev/null
+++ b/gd/neighbor/name_db.cc
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "bt_gd_neigh"
+
+#include "neighbor/name_db.h"
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bind.h"
+#include "module.h"
+#include "neighbor/name.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+namespace {
+struct PendingRemoteNameRead {
+  ReadRemoteNameDbCallback callback_;
+  os::Handler* handler_;
+};
+}  // namespace
+
+struct NameDbModule::impl {
+  void ReadRemoteNameRequest(hci::Address address, ReadRemoteNameDbCallback callback, os::Handler* handler);
+
+  bool IsNameCached(hci::Address address) const;
+  RemoteName ReadCachedRemoteName(hci::Address address) const;
+
+  impl(const NameDbModule& module);
+
+  void Start();
+  void Stop();
+
+ private:
+  std::unordered_map<hci::Address, PendingRemoteNameRead> address_to_pending_read_map_;
+  std::unordered_map<hci::Address, RemoteName> address_to_name_map_;
+
+  void OnRemoteNameResponse(hci::ErrorCode status, hci::Address address, RemoteName name);
+
+  neighbor::NameModule* name_module_;
+
+  const NameDbModule& module_;
+  os::Handler* handler_;
+};
+
+const ModuleFactory neighbor::NameDbModule::Factory = ModuleFactory([]() { return new neighbor::NameDbModule(); });
+
+neighbor::NameDbModule::impl::impl(const neighbor::NameDbModule& module) : module_(module) {}
+
+void neighbor::NameDbModule::impl::ReadRemoteNameRequest(
+    hci::Address address, ReadRemoteNameDbCallback callback, os::Handler* handler) {
+  if (address_to_pending_read_map_.find(address) != address_to_pending_read_map_.end()) {
+    LOG_WARN("Already have remote read db in progress and currently can only have one outstanding");
+    return;
+  }
+
+  address_to_pending_read_map_[address] = {std::move(callback), std::move(handler)};
+
+  // TODO(cmanton) Use remote name request defaults for now
+  hci::PageScanRepetitionMode page_scan_repetition_mode = hci::PageScanRepetitionMode::R1;
+  uint16_t clock_offset = 0;
+  hci::ClockOffsetValid clock_offset_valid = hci::ClockOffsetValid::INVALID;
+  name_module_->ReadRemoteNameRequest(
+      address,
+      page_scan_repetition_mode,
+      clock_offset,
+      clock_offset_valid,
+      common::BindOnce(&NameDbModule::impl::OnRemoteNameResponse, common::Unretained(this)),
+      handler_);
+}
+
+void neighbor::NameDbModule::impl::OnRemoteNameResponse(hci::ErrorCode status, hci::Address address, RemoteName name) {
+  ASSERT(address_to_pending_read_map_.find(address) != address_to_pending_read_map_.end());
+  PendingRemoteNameRead callback_handler = std::move(address_to_pending_read_map_.at(address));
+
+  if (status == hci::ErrorCode::SUCCESS) {
+    address_to_name_map_[address] = name;
+  }
+  callback_handler.handler_->Post(
+      common::BindOnce(std::move(callback_handler.callback_), address, status == hci::ErrorCode::SUCCESS));
+}
+
+bool neighbor::NameDbModule::impl::IsNameCached(hci::Address address) const {
+  return address_to_name_map_.count(address) == 1;
+}
+
+RemoteName neighbor::NameDbModule::impl::ReadCachedRemoteName(hci::Address address) const {
+  ASSERT(IsNameCached(address));
+  return address_to_name_map_.at(address);
+}
+
+/**
+ * General API here
+ */
+neighbor::NameDbModule::NameDbModule() : pimpl_(std::make_unique<impl>(*this)) {}
+
+neighbor::NameDbModule::~NameDbModule() {
+  pimpl_.reset();
+}
+
+void neighbor::NameDbModule::ReadRemoteNameRequest(
+    hci::Address address, ReadRemoteNameDbCallback callback, os::Handler* handler) {
+  GetHandler()->Post(common::BindOnce(
+      &NameDbModule::impl::ReadRemoteNameRequest,
+      common::Unretained(pimpl_.get()),
+      address,
+      std::move(callback),
+      handler));
+}
+
+bool neighbor::NameDbModule::IsNameCached(hci::Address address) const {
+  return pimpl_->IsNameCached(address);
+}
+
+RemoteName neighbor::NameDbModule::ReadCachedRemoteName(hci::Address address) const {
+  return pimpl_->ReadCachedRemoteName(address);
+}
+
+void neighbor::NameDbModule::impl::Start() {
+  name_module_ = module_.GetDependency<neighbor::NameModule>();
+  handler_ = module_.GetHandler();
+}
+
+void neighbor::NameDbModule::impl::Stop() {}
+
+/**
+ * Module methods here
+ */
+void neighbor::NameDbModule::ListDependencies(ModuleList* list) {
+  list->add<neighbor::NameModule>();
+}
+
+void neighbor::NameDbModule::Start() {
+  pimpl_->Start();
+}
+
+void neighbor::NameDbModule::Stop() {
+  pimpl_->Stop();
+}
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/name_db.h b/gd/neighbor/name_db.h
new file mode 100644
index 0000000..010c4941
--- /dev/null
+++ b/gd/neighbor/name_db.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <memory>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "neighbor/name.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+using ReadRemoteNameDbCallback = common::OnceCallback<void(hci::Address address, bool success)>;
+
+class NameDbModule : public bluetooth::Module {
+ public:
+  void ReadRemoteNameRequest(hci::Address address, ReadRemoteNameDbCallback callback, os::Handler* handler);
+
+  bool IsNameCached(hci::Address address) const;
+  RemoteName ReadCachedRemoteName(hci::Address address) const;
+
+  static const ModuleFactory Factory;
+
+  NameDbModule();
+  ~NameDbModule();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(NameDbModule);
+};
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/page.cc b/gd/neighbor/page.cc
index 4631875..aeb95a7 100644
--- a/gd/neighbor/page.cc
+++ b/gd/neighbor/page.cc
@@ -15,13 +15,14 @@
  */
 #define LOG_TAG "bt_gd_neigh"
 
+#include "neighbor/page.h"
+
 #include <memory>
 
 #include "common/bind.h"
 #include "hci/hci_layer.h"
 #include "hci/hci_packets.h"
 #include "module.h"
-#include "neighbor/page.h"
 #include "neighbor/scan_parameters.h"
 #include "os/handler.h"
 #include "os/log.h"
@@ -111,14 +112,14 @@
   hci_layer_ = module_.GetDependency<hci::HciLayer>();
   handler_ = module_.GetHandler();
 
-  hci_layer_->EnqueueCommand(hci::ReadPageScanActivityBuilder::Create(),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::ReadPageScanActivityBuilder::Create(), handler_->BindOnceOn(this, &impl::OnCommandComplete));
 
-  hci_layer_->EnqueueCommand(hci::ReadPageScanTypeBuilder::Create(),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::ReadPageScanTypeBuilder::Create(), handler_->BindOnceOn(this, &impl::OnCommandComplete));
 
-  hci_layer_->EnqueueCommand(hci::ReadPageTimeoutBuilder::Create(),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::ReadPageTimeoutBuilder::Create(), handler_->BindOnceOn(this, &impl::OnCommandComplete));
 }
 
 void neighbor::PageModule::impl::Stop() {
@@ -127,13 +128,18 @@
 }
 
 void neighbor::PageModule::impl::SetScanActivity(ScanParameters params) {
-  hci_layer_->EnqueueCommand(hci::WritePageScanActivityBuilder::Create(params.interval, params.window),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::WritePageScanActivityBuilder::Create(params.interval, params.window),
+      handler_->BindOnceOn(this, &impl::OnCommandComplete));
 
-  hci_layer_->EnqueueCommand(hci::ReadPageScanActivityBuilder::Create(),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
-  LOG_DEBUG("Set page scan activity interval:0x%x/%.02fms window:0x%x/%.02fms", params.interval,
-            ScanIntervalTimeMs(params.interval), params.window, ScanWindowTimeMs(params.window));
+  hci_layer_->EnqueueCommand(
+      hci::ReadPageScanActivityBuilder::Create(), handler_->BindOnceOn(this, &impl::OnCommandComplete));
+  LOG_DEBUG(
+      "Set page scan activity interval:0x%x/%.02fms window:0x%x/%.02fms",
+      params.interval,
+      ScanIntervalTimeMs(params.interval),
+      params.window,
+      ScanWindowTimeMs(params.window));
 }
 
 ScanParameters neighbor::PageModule::impl::GetScanActivity() const {
@@ -141,20 +147,20 @@
 }
 
 void neighbor::PageModule::impl::SetScanType(hci::PageScanType scan_type) {
-  hci_layer_->EnqueueCommand(hci::WritePageScanTypeBuilder::Create(scan_type),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::WritePageScanTypeBuilder::Create(scan_type), handler_->BindOnceOn(this, &impl::OnCommandComplete));
 
-  hci_layer_->EnqueueCommand(hci::ReadPageScanTypeBuilder::Create(),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::ReadPageScanTypeBuilder::Create(), handler_->BindOnceOn(this, &impl::OnCommandComplete));
   LOG_DEBUG("Set page scan type:%s", hci::PageScanTypeText(scan_type).c_str());
 }
 
 void neighbor::PageModule::impl::SetTimeout(PageTimeout timeout) {
-  hci_layer_->EnqueueCommand(hci::WritePageTimeoutBuilder::Create(timeout),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::WritePageTimeoutBuilder::Create(timeout), handler_->BindOnceOn(this, &impl::OnCommandComplete));
 
-  hci_layer_->EnqueueCommand(hci::ReadPageTimeoutBuilder::Create(),
-                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(
+      hci::ReadPageTimeoutBuilder::Create(), handler_->BindOnceOn(this, &impl::OnCommandComplete));
   LOG_DEBUG("Set page scan timeout:0x%x/%.02fms", timeout, PageTimeoutMs(timeout));
 }
 
diff --git a/gd/neighbor/scan.cc b/gd/neighbor/scan.cc
index 5232b08..45a7748 100644
--- a/gd/neighbor/scan.cc
+++ b/gd/neighbor/scan.cc
@@ -16,7 +16,9 @@
 #define LOG_TAG "bt_gd_neigh"
 
 #include "neighbor/scan.h"
+
 #include <memory>
+
 #include "hci/hci_layer.h"
 #include "hci/hci_packets.h"
 #include "module.h"
@@ -94,14 +96,12 @@
 
   {
     std::unique_ptr<hci::WriteScanEnableBuilder> packet = hci::WriteScanEnableBuilder::Create(scan_enable);
-    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
-                               handler_);
+    hci_layer_->EnqueueCommand(std::move(packet), handler_->BindOnceOn(this, &impl::OnCommandComplete));
   }
 
   {
     std::unique_ptr<hci::ReadScanEnableBuilder> packet = hci::ReadScanEnableBuilder::Create();
-    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
-                               handler_);
+    hci_layer_->EnqueueCommand(std::move(packet), handler_->BindOnceOn(this, &impl::OnCommandComplete));
   }
 }
 
@@ -152,8 +152,7 @@
   handler_ = module_.GetHandler();
 
   std::unique_ptr<hci::ReadScanEnableBuilder> packet = hci::ReadScanEnableBuilder::Create();
-  hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
-                             handler_);
+  hci_layer_->EnqueueCommand(std::move(packet), handler_->BindOnceOn(this, &impl::OnCommandComplete));
 }
 
 void neighbor::ScanModule::impl::Stop() {
diff --git a/gd/os/Android.bp b/gd/os/Android.bp
index b8ad91a..dce77ee 100644
--- a/gd/os/Android.bp
+++ b/gd/os/Android.bp
@@ -1,7 +1,31 @@
 filegroup {
+    name: "BluetoothOsSources_android",
+    srcs: [
+        "android/parameter_provider.cc",
+        "android/system_properties.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothOsTestSources_android",
+    srcs: [
+        "android/system_properties_test.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothOsSources_host",
+    srcs: [
+        "host/parameter_provider.cc",
+        "host/system_properties.cc",
+    ],
+}
+
+filegroup {
     name: "BluetoothOsSources_linux_generic",
     srcs: [
         "linux_generic/alarm.cc",
+        "linux_generic/files.cc",
         "linux_generic/handler.cc",
         "linux_generic/reactor.cc",
         "linux_generic/repeating_alarm.cc",
@@ -14,6 +38,7 @@
     name: "BluetoothOsTestSources_linux_generic",
     srcs: [
         "linux_generic/alarm_unittest.cc",
+        "linux_generic/files_test.cc",
         "linux_generic/handler_unittest.cc",
         "linux_generic/queue_unittest.cc",
         "linux_generic/reactor_unittest.cc",
@@ -23,10 +48,17 @@
 }
 
 filegroup {
-    name: "BluetoothOsBenchmarkSources",
+name: "BluetoothOsBenchmarkSources",
+     srcs: [
+         "alarm_benchmark.cc",
+         "thread_benchmark.cc",
+         "queue_benchmark.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothOsSources_fuzz",
     srcs: [
-        "alarm_benchmark.cc",
-        "thread_benchmark.cc",
-        "queue_benchmark.cc",
+        "fuzz/fake_timerfd.cc",
     ],
 }
diff --git a/gd/os/alarm.h b/gd/os/alarm.h
index e529fb9..773c36f 100644
--- a/gd/os/alarm.h
+++ b/gd/os/alarm.h
@@ -42,13 +42,13 @@
   DISALLOW_COPY_AND_ASSIGN(Alarm);
 
   // Schedule the alarm with given delay
-  void Schedule(OnceClosure task, std::chrono::milliseconds delay);
+  void Schedule(common::OnceClosure task, std::chrono::milliseconds delay);
 
   // Cancel the alarm. No-op if it's not armed.
   void Cancel();
 
  private:
-  OnceClosure task_;
+  common::OnceClosure task_;
   Handler* handler_;
   int fd_ = 0;
   Reactor::Reactable* token_;
diff --git a/gd/os/alarm_benchmark.cc b/gd/os/alarm_benchmark.cc
index 2150625..286ae4e 100644
--- a/gd/os/alarm_benchmark.cc
+++ b/gd/os/alarm_benchmark.cc
@@ -19,7 +19,6 @@
 #include <unordered_map>
 
 #include "benchmark/benchmark.h"
-
 #include "common/bind.h"
 #include "os/alarm.h"
 #include "os/repeating_alarm.h"
@@ -117,9 +116,11 @@
     task_length_ = state.range(1);
     task_interval_ = state.range(2);
     start_time_ = std::chrono::steady_clock::now();
-    repeating_alarm_->Schedule(Bind(&BM_ReactableAlarm_periodic_accuracy_Benchmark::AlarmSleepAndCountDelayedTime,
-                                    bluetooth::common::Unretained(this)),
-                               std::chrono::milliseconds(task_interval_));
+    repeating_alarm_->Schedule(
+        Bind(
+            &BM_ReactableAlarm_periodic_accuracy_Benchmark::AlarmSleepAndCountDelayedTime,
+            bluetooth::common::Unretained(this)),
+        std::chrono::milliseconds(task_interval_));
     promise_.get_future().get();
     repeating_alarm_->Cancel();
   }
diff --git a/gd/l2cap/security_policy.h b/gd/os/android/parameter_provider.cc
similarity index 73%
copy from gd/l2cap/security_policy.h
copy to gd/os/android/parameter_provider.cc
index 5a06401..965123f 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/os/android/parameter_provider.cc
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
+
+#include "os/parameter_provider.h"
 
 namespace bluetooth {
-namespace l2cap {
+namespace os {
 
-class SecurityPolicy {};
+// On Android we always write a single default location
+std::string ParameterProvider::ConfigFilePath() {
+  return "/data/misc/bluedroid/bt_config.conf";
+}
 
-}  // namespace l2cap
+}  // namespace os
 }  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/os/android/system_properties.cc b/gd/os/android/system_properties.cc
new file mode 100644
index 0000000..a76b809
--- /dev/null
+++ b/gd/os/android/system_properties.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "os/system_properties.h"
+
+#include <cutils/properties.h>
+
+#include <array>
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace os {
+
+std::optional<std::string> GetSystemProperty(const std::string& property) {
+  if (property.size() >= PROP_NAME_MAX) {
+    LOG_ERROR("Property name's maximum size is %d, but %zu chars were given", PROP_NAME_MAX - 1, property.size());
+    return std::nullopt;
+  }
+  std::array<char, PROPERTY_VALUE_MAX> value_array{0};
+  auto value_len = property_get(property.c_str(), value_array.data(), nullptr);
+  if (value_len <= 0) {
+    return std::nullopt;
+  }
+  return std::string(value_array.data(), value_len);
+}
+
+bool SetSystemProperty(const std::string& property, const std::string& value) {
+  if (property.size() >= PROP_NAME_MAX) {
+    LOG_ERROR("Property name's maximum size is %d, but %zu chars were given", PROP_NAME_MAX - 1, property.size());
+    return false;
+  }
+  if (value.size() >= PROPERTY_VALUE_MAX) {
+    LOG_ERROR("Property value's maximum size is %d, but %zu chars were given", PROPERTY_VALUE_MAX - 1, value.size());
+    return false;
+  }
+  auto ret = property_set(property.c_str(), value.c_str());
+  if (ret != 0) {
+    LOG_ERROR("Set property %s failed with error code %d", property.c_str(), ret);
+    return false;
+  }
+  return true;
+}
+
+}  // namespace os
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/os/android/system_properties_test.cc b/gd/os/android/system_properties_test.cc
new file mode 100644
index 0000000..c509e14
--- /dev/null
+++ b/gd/os/android/system_properties_test.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <cutils/properties.h>
+
+#include "os/system_properties.h"
+
+namespace testing {
+
+using bluetooth::os::GetSystemProperty;
+using bluetooth::os::SetSystemProperty;
+
+TEST(SystemPropertiesTest, set_and_get_test) {
+  ASSERT_TRUE(SetSystemProperty("persist.bluetooth.factoryreset", "true"));
+  auto ret = GetSystemProperty("persist.bluetooth.factoryreset");
+  ASSERT_TRUE(ret);
+  ASSERT_EQ(ret, "true");
+  ASSERT_TRUE(SetSystemProperty("persist.bluetooth.factoryreset", "false"));
+  ret = GetSystemProperty("persist.bluetooth.factoryreset");
+  ASSERT_TRUE(ret);
+  ASSERT_EQ(ret, "false");
+  ret = GetSystemProperty("persist.bluetooth.factoryreset_do_not_exist");
+  ASSERT_FALSE(ret);
+}
+
+TEST(SystemPropertiesTest, max_length_test) {
+  std::string property(PROP_NAME_MAX, 'a');
+  std::string value(PROP_VALUE_MAX, '1');
+  ASSERT_TRUE(SetSystemProperty("persist.bluetooth.factoryreset", "false"));
+  ASSERT_FALSE(SetSystemProperty(property, "true"));
+  ASSERT_FALSE(SetSystemProperty("persist.bluetooth.factoryreset", value));
+  ASSERT_FALSE(SetSystemProperty(property, value));
+  ASSERT_FALSE(GetSystemProperty(property));
+  // make sure no actual operations on system property happened
+  auto ret = GetSystemProperty("persist.bluetooth.factoryreset");
+  ASSERT_TRUE(ret);
+  ASSERT_EQ(ret, "false");
+}
+
+}  // namespace testing
\ No newline at end of file
diff --git a/gd/os/files.h b/gd/os/files.h
new file mode 100644
index 0000000..42fd8cf
--- /dev/null
+++ b/gd/os/files.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <iterator>
+#include <optional>
+
+namespace bluetooth {
+namespace os {
+
+// Return true if |path| exists on disk
+bool FileExists(const std::string& path);
+
+// Rename file from |from| to |to|
+bool RenameFile(const std::string& from, const std::string& to);
+
+// Implement ability to read a whole file from |path| into a C++ string, return std::nullopt on failure
+//
+// Do not use this with large files
+std::optional<std::string> ReadSmallFile(const std::string& path);
+
+// Implement ability to safely write to a file. This function is needed because of deficiencies in existing C++ file
+// libraries, namely:
+// - The ability to open and sync directories with storage media
+// - The ability to block and sync file to storage media
+// Return true on success, false on failure
+bool WriteToFile(const std::string& path, const std::string& data);
+
+}  // namespace os
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/os/fuzz/dev_null_queue.h b/gd/os/fuzz/dev_null_queue.h
new file mode 100644
index 0000000..0b2d7c5
--- /dev/null
+++ b/gd/os/fuzz/dev_null_queue.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "os/queue.h"
+
+namespace bluetooth {
+namespace os {
+namespace fuzz {
+
+// Drops stuff you send it, and banishes it into the void.
+template <typename T>
+class DevNullQueue {
+ public:
+  DevNullQueue(IQueueDequeue<T>* queue, Handler* handler) : queue_(queue), handler_(handler) {}
+  ~DevNullQueue() {}
+
+  void Start() {
+    queue_->RegisterDequeue(handler_, common::Bind(&DevNullQueue::Dump, common::Unretained(this)));
+  }
+
+  void Stop() {
+    queue_->UnregisterDequeue();
+  }
+
+  void Dump() {
+    queue_->TryDequeue();
+  }
+
+ private:
+  IQueueDequeue<T>* queue_;
+  Handler* handler_;
+};
+
+}  // namespace fuzz
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/fuzz/fake_timerfd.cc b/gd/os/fuzz/fake_timerfd.cc
new file mode 100644
index 0000000..5b7389b
--- /dev/null
+++ b/gd/os/fuzz/fake_timerfd.cc
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#include "os/fuzz/fake_timerfd.h"
+
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+#include <map>
+
+namespace bluetooth {
+namespace os {
+namespace fuzz {
+
+class FakeTimerFd {
+ public:
+  int fd;
+  bool active;
+  uint64_t trigger_ms;
+  uint64_t period_ms;
+};
+
+static std::map<int, FakeTimerFd*> fake_timers;
+static uint64_t clock = 0;
+static uint64_t max_clock = UINT64_MAX;
+
+static uint64_t timespec_to_ms(const timespec* t) {
+  return t->tv_sec * 1000 + t->tv_nsec / 1000000;
+}
+
+int fake_timerfd_create(int clockid, int flags) {
+  int fd = eventfd(0, 0);
+  if (fd == -1) {
+    return fd;
+  }
+
+  FakeTimerFd* entry = new FakeTimerFd();
+  fake_timers[fd] = entry;
+  entry->fd = fd;
+  return fd;
+}
+
+int fake_timerfd_settime(int fd, int flags, const struct itimerspec* new_value, struct itimerspec* old_value) {
+  if (fake_timers.find(fd) == fake_timers.end()) {
+    return -1;
+  }
+
+  FakeTimerFd* entry = fake_timers[fd];
+
+  uint64_t trigger_delta_ms = timespec_to_ms(&new_value->it_value);
+  entry->active = trigger_delta_ms != 0;
+  if (!entry->active) {
+    return 0;
+  }
+
+  uint64_t period_ms = timespec_to_ms(&new_value->it_value);
+  entry->trigger_ms = clock + trigger_delta_ms;
+  entry->period_ms = period_ms;
+  return 0;
+}
+
+int fake_timerfd_close(int fd) {
+  auto timer_iterator = fake_timers.find(fd);
+  if (timer_iterator != fake_timers.end()) {
+    delete timer_iterator->second;
+    fake_timers.erase(timer_iterator);
+  }
+  return close(fd);
+}
+
+void fake_timerfd_reset() {
+  clock = 0;
+  max_clock = UINT64_MAX;
+  // if there are entries still here, it is a failure of our users to clean up
+  // so let them leak and trigger errors
+  fake_timers.clear();
+}
+
+static bool fire_next_event(uint64_t new_clock) {
+  uint64_t earliest_time = new_clock;
+  FakeTimerFd* to_fire = nullptr;
+  for (auto it = fake_timers.begin(); it != fake_timers.end(); it++) {
+    FakeTimerFd* entry = it->second;
+    if (!entry->active) {
+      continue;
+    }
+
+    if (entry->trigger_ms > clock && entry->trigger_ms <= new_clock) {
+      if (to_fire == nullptr || entry->trigger_ms < earliest_time) {
+        to_fire = entry;
+        earliest_time = entry->trigger_ms;
+      }
+    }
+  }
+
+  if (to_fire == nullptr) {
+    return false;
+  }
+
+  bool is_periodic = to_fire->period_ms != 0;
+  if (is_periodic) {
+    to_fire->trigger_ms += to_fire->period_ms;
+  }
+  to_fire->active = is_periodic;
+  uint64_t value = 1;
+  write(to_fire->fd, &value, sizeof(uint64_t));
+  return true;
+}
+
+void fake_timerfd_advance(uint64_t ms) {
+  uint64_t new_clock = clock + ms;
+  if (new_clock > max_clock) {
+    new_clock = max_clock;
+  }
+  while (fire_next_event(new_clock)) {
+  }
+  clock = new_clock;
+}
+
+void fake_timerfd_cap_at(uint64_t ms) {
+  max_clock = ms;
+}
+
+}  // namespace fuzz
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/fuzz/fake_timerfd.h b/gd/os/fuzz/fake_timerfd.h
new file mode 100644
index 0000000..069e153
--- /dev/null
+++ b/gd/os/fuzz/fake_timerfd.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/timerfd.h>
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace os {
+namespace fuzz {
+
+int fake_timerfd_create(int clockid, int flags);
+
+int fake_timerfd_settime(int fd, int flags, const struct itimerspec* new_value, struct itimerspec* old_value);
+
+int fake_timerfd_close(int fd);
+
+void fake_timerfd_reset();
+
+void fake_timerfd_advance(uint64_t ms);
+
+void fake_timerfd_cap_at(uint64_t ms);
+
+}  // namespace fuzz
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/fuzz/fuzz_inject_queue.h b/gd/os/fuzz/fuzz_inject_queue.h
new file mode 100644
index 0000000..2b9ebae
--- /dev/null
+++ b/gd/os/fuzz/fuzz_inject_queue.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "os/queue.h"
+
+namespace bluetooth {
+namespace os {
+namespace fuzz {
+
+template <typename T>
+class FuzzInjectQueue {
+ public:
+  FuzzInjectQueue(IQueueEnqueue<T>* queue, Handler* handler) : handler_(handler) {
+    buffer_ = new EnqueueBuffer<T>(queue);
+  }
+  ~FuzzInjectQueue() {
+    delete buffer_;
+  }
+
+  void Inject(std::unique_ptr<T> data) {
+    buffer_->Enqueue(std::move(data), handler_);
+  }
+
+ private:
+  EnqueueBuffer<T>* buffer_;
+  Handler* handler_;
+};
+
+}  // namespace fuzz
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/handler.h b/gd/os/handler.h
index d9dea0b..9e530bc 100644
--- a/gd/os/handler.h
+++ b/gd/os/handler.h
@@ -21,7 +21,9 @@
 #include <mutex>
 #include <queue>
 
+#include "common/bind.h"
 #include "common/callback.h"
+#include "common/contextual_callback.h"
 #include "os/thread.h"
 #include "os/utils.h"
 
@@ -31,18 +33,18 @@
 // A message-queue style handler for reactor-based thread to handle incoming events from different threads. When it's
 // constructed, it will register a reactable on the specified thread; when it's destroyed, it will unregister itself
 // from the thread.
-class Handler {
+class Handler : public common::IPostableContext {
  public:
   // Create and register a handler on given thread
   explicit Handler(Thread* thread);
 
   // Unregister this handler from the thread and release resource. Unhandled events will be discarded and not executed.
-  ~Handler();
+  virtual ~Handler();
 
   DISALLOW_COPY_AND_ASSIGN(Handler);
 
   // Enqueue a closure to the queue of this handler
-  void Post(OnceClosure closure);
+  virtual void Post(common::OnceClosure closure) override;
 
   // Remove all pending events from the queue of this handler
   void Clear();
@@ -50,6 +52,43 @@
   // Die if the current reactable doesn't stop before the timeout.  Must be called after Clear()
   void WaitUntilStopped(std::chrono::milliseconds timeout);
 
+  template <typename Functor, typename... Args>
+  void Call(Functor&& functor, Args&&... args) {
+    Post(common::BindOnce(std::forward<Functor>(functor), std::forward<Args>(args)...));
+  }
+
+  template <typename T, typename Functor, typename... Args>
+  void CallOn(T* obj, Functor&& functor, Args&&... args) {
+    Post(common::BindOnce(std::forward<Functor>(functor), common::Unretained(obj), std::forward<Args>(args)...));
+  }
+
+  template <typename Functor, typename... Args>
+  common::ContextualOnceCallback<common::MakeUnboundRunType<Functor, Args...>> BindOnce(
+      Functor&& functor, Args&&... args) {
+    return common::ContextualOnceCallback<common::MakeUnboundRunType<Functor, Args...>>(
+        common::BindOnce(std::forward<Functor>(functor), std::forward<Args>(args)...), this);
+  }
+
+  template <typename Functor, typename T, typename... Args>
+  common::ContextualOnceCallback<common::MakeUnboundRunType<Functor, T, Args...>> BindOnceOn(
+      T* obj, Functor&& functor, Args&&... args) {
+    return common::ContextualOnceCallback<common::MakeUnboundRunType<Functor, T, Args...>>(
+        common::BindOnce(std::forward<Functor>(functor), common::Unretained(obj), std::forward<Args>(args)...), this);
+  }
+
+  template <typename Functor, typename... Args>
+  common::ContextualCallback<common::MakeUnboundRunType<Functor, Args...>> Bind(Functor&& functor, Args&&... args) {
+    return common::ContextualCallback<common::MakeUnboundRunType<Functor, Args...>>(
+        common::Bind(std::forward<Functor>(functor), std::forward<Args>(args)...), this);
+  }
+
+  template <typename Functor, typename T, typename... Args>
+  common::ContextualCallback<common::MakeUnboundRunType<Functor, T, Args...>> BindOn(
+      T* obj, Functor&& functor, Args&&... args) {
+    return common::ContextualCallback<common::MakeUnboundRunType<Functor, T, Args...>>(
+        common::Bind(std::forward<Functor>(functor), common::Unretained(obj), std::forward<Args>(args)...), this);
+  }
+
   template <typename T>
   friend class Queue;
 
@@ -61,7 +100,7 @@
   inline bool was_cleared() const {
     return tasks_ == nullptr;
   };
-  std::queue<OnceClosure>* tasks_;
+  std::queue<common::OnceClosure>* tasks_;
   Thread* thread_;
   int fd_;
   Reactor::Reactable* reactable_;
diff --git a/gd/os/host/parameter_provider.cc b/gd/os/host/parameter_provider.cc
new file mode 100644
index 0000000..a5b5cef
--- /dev/null
+++ b/gd/os/host/parameter_provider.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "os/parameter_provider.h"
+
+#include <unistd.h>
+
+#include <cerrno>
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace os {
+
+// Write to $PWD/bt_stack.conf if $PWD can be found, otherwise, write to $HOME/bt_stack.conf
+std::string ParameterProvider::ConfigFilePath() {
+  char cwd[PATH_MAX] = {};
+  if (getcwd(cwd, sizeof(cwd)) == nullptr) {
+    LOG_ERROR("Failed to get current working directory due to \"%s\", returning default", strerror(errno));
+    return "bt_config.conf";
+  }
+  return std::string(cwd) + "/bt_config.conf";
+}
+
+}  // namespace os
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/security_policy.h b/gd/os/host/system_properties.cc
similarity index 70%
copy from gd/l2cap/security_policy.h
copy to gd/os/host/system_properties.cc
index 5a06401..ce8f308 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/os/host/system_properties.cc
@@ -13,12 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
+
+#include "os/system_properties.h"
 
 namespace bluetooth {
-namespace l2cap {
+namespace os {
 
-class SecurityPolicy {};
+std::optional<std::string> GetSystemProperty(const std::string& property) {
+  return std::nullopt;
+}
 
-}  // namespace l2cap
+bool SetSystemProperty(const std::string& property, const std::string& value) {
+  return false;
+}
+
+}  // namespace os
 }  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/os/linux_generic/alarm.cc b/gd/os/linux_generic/alarm.cc
index a2d895e..cd859b5 100644
--- a/gd/os/linux_generic/alarm.cc
+++ b/gd/os/linux_generic/alarm.cc
@@ -17,10 +17,12 @@
 #include "os/alarm.h"
 
 #include <sys/timerfd.h>
-#include <cstring>
 #include <unistd.h>
 
+#include <cstring>
+
 #include "common/bind.h"
+#include "os/linux_generic/linux.h"
 #include "os/log.h"
 #include "os/utils.h"
 
@@ -32,30 +34,29 @@
 
 namespace bluetooth {
 namespace os {
+using common::Closure;
+using common::OnceClosure;
 
-Alarm::Alarm(Handler* handler) : handler_(handler), fd_(timerfd_create(ALARM_CLOCK, 0)) {
+Alarm::Alarm(Handler* handler) : handler_(handler), fd_(TIMERFD_CREATE(ALARM_CLOCK, 0)) {
   ASSERT_LOG(fd_ != -1, "cannot create timerfd: %s", strerror(errno));
 
-  token_ = handler_->thread_->GetReactor()->Register(fd_, common::Bind(&Alarm::on_fire, common::Unretained(this)),
-                                                     Closure());
+  token_ = handler_->thread_->GetReactor()->Register(
+      fd_, common::Bind(&Alarm::on_fire, common::Unretained(this)), Closure());
 }
 
 Alarm::~Alarm() {
   handler_->thread_->GetReactor()->Unregister(token_);
 
   int close_status;
-  RUN_NO_INTR(close_status = close(fd_));
+  RUN_NO_INTR(close_status = TIMERFD_CLOSE(fd_));
   ASSERT(close_status != -1);
 }
 
 void Alarm::Schedule(OnceClosure task, std::chrono::milliseconds delay) {
   std::lock_guard<std::mutex> lock(mutex_);
   long delay_ms = delay.count();
-  itimerspec timer_itimerspec{
-    {/* interval for periodic timer */},
-    {delay_ms / 1000, delay_ms % 1000 * 1000000}
-  };
-  int result = timerfd_settime(fd_, 0, &timer_itimerspec, nullptr);
+  itimerspec timer_itimerspec{{/* interval for periodic timer */}, {delay_ms / 1000, delay_ms % 1000 * 1000000}};
+  int result = TIMERFD_SETTIME(fd_, 0, &timer_itimerspec, nullptr);
   ASSERT(result == 0);
 
   task_ = std::move(task);
@@ -64,7 +65,7 @@
 void Alarm::Cancel() {
   std::lock_guard<std::mutex> lock(mutex_);
   itimerspec disarm_itimerspec{/* disarm timer */};
-  int result = timerfd_settime(fd_, 0, &disarm_itimerspec, nullptr);
+  int result = TIMERFD_SETTIME(fd_, 0, &disarm_itimerspec, nullptr);
   ASSERT(result == 0);
 }
 
diff --git a/gd/os/linux_generic/alarm_unittest.cc b/gd/os/linux_generic/alarm_unittest.cc
index b389a82..9b4a5e5 100644
--- a/gd/os/linux_generic/alarm_unittest.cc
+++ b/gd/os/linux_generic/alarm_unittest.cc
@@ -58,8 +58,8 @@
   auto before = std::chrono::steady_clock::now();
   int delay_ms = 10;
   int delay_error_ms = 3;
-  alarm_->Schedule(BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)),
-                   std::chrono::milliseconds(delay_ms));
+  alarm_->Schedule(
+      BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)), std::chrono::milliseconds(delay_ms));
   future.get();
   auto after = std::chrono::steady_clock::now();
   auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
@@ -81,8 +81,8 @@
   alarm_->Schedule(BindOnce([]() { ASSERT_TRUE(false) << "Should not happen"; }), std::chrono::milliseconds(1));
   std::promise<void> promise;
   auto future = promise.get_future();
-  alarm_->Schedule(BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)),
-                   std::chrono::milliseconds(10));
+  alarm_->Schedule(
+      BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)), std::chrono::milliseconds(10));
   future.get();
 }
 
diff --git a/gd/os/linux_generic/files.cc b/gd/os/linux_generic/files.cc
new file mode 100644
index 0000000..c81a681
--- /dev/null
+++ b/gd/os/linux_generic/files.cc
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+#include "os/files.h"
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstring>
+#include <fstream>
+#include <streambuf>
+#include <string>
+
+#include "os/log.h"
+
+namespace {
+
+void HandleError(const std::string& temp_path, int* dir_fd, FILE** fp) {
+  // This indicates there is a write issue.  Unlink as partial data is not
+  // acceptable.
+  unlink(temp_path.c_str());
+  if (*fp) {
+    fclose(*fp);
+    *fp = nullptr;
+  }
+  if (*dir_fd != -1) {
+    close(*dir_fd);
+    *dir_fd = -1;
+  }
+}
+
+}  // namespace
+
+namespace bluetooth {
+namespace os {
+
+bool FileExists(const std::string& path) {
+  std::ifstream input(path, std::ios::binary | std::ios::ate);
+  return input.good();
+}
+
+bool RenameFile(const std::string& from, const std::string& to) {
+  if (std::rename(from.c_str(), to.c_str()) != 0) {
+    LOG_ERROR("unable to rename file from '%s' to '%s', error: %s", from.c_str(), to.c_str(), strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+std::optional<std::string> ReadSmallFile(const std::string& path) {
+  std::ifstream input(path, std::ios::binary | std::ios::ate);
+  if (!input) {
+    LOG_WARN("Failed to open file '%s', error: %s", path.c_str(), strerror(errno));
+    return std::nullopt;
+  }
+  auto file_size = input.tellg();
+  if (file_size < 0) {
+    LOG_WARN("Failed to get file size for '%s', error: %s", path.c_str(), strerror(errno));
+    return std::nullopt;
+  }
+  std::string result(file_size, '\0');
+  if (!input.seekg(0)) {
+    LOG_WARN("Failed to go back to the beginning of file '%s', error: %s", path.c_str(), strerror(errno));
+    return std::nullopt;
+  }
+  if (!input.read(result.data(), result.size())) {
+    LOG_WARN("Failed to read file '%s', error: %s", path.c_str(), strerror(errno));
+    return std::nullopt;
+  }
+  input.close();
+  return result;
+}
+
+bool WriteToFile(const std::string& path, const std::string& data) {
+  ASSERT(!path.empty());
+  // Steps to ensure content of data gets to disk:
+  //
+  // 1) Open and write to temp file (e.g. bt_config.conf.new).
+  // 2) Flush the stream buffer to the temp file.
+  // 3) Sync the temp file to disk with fsync().
+  // 4) Rename temp file to actual config file (e.g. bt_config.conf).
+  //    This ensures atomic update.
+  // 5) Sync directory that has the conf file with fsync().
+  //    This ensures directory entries are up-to-date.
+  //
+  // We are using traditional C type file methods because C++ std::filesystem and std::ofstream do not support:
+  // - Operation on directories
+  // - fsync() to ensure content is written to disk
+
+  // Build temp config file based on config file (e.g. bt_config.conf.new).
+  const std::string temp_path = path + ".new";
+
+  // Extract directory from file path (e.g. /data/misc/bluedroid).
+  // libc++fs is not supported in APEX yet and hence cannot use std::filesystem::path::parent_path
+  std::string directory_path;
+  {
+    // Make a temporary variable as inputs to dirname() will be modified and return value points to input char array
+    // temp_path_for_dir must not be destroyed until results from dirname is appended to directory_path
+    std::string temp_path_for_dir(path);
+    directory_path.append(dirname(temp_path_for_dir.data()));
+  }
+  if (directory_path.empty()) {
+    LOG_ERROR("error extracting directory from '%s', error: %s", path.c_str(), strerror(errno));
+    return false;
+  }
+
+  int dir_fd = open(directory_path.c_str(), O_RDONLY | O_DIRECTORY);
+  if (dir_fd < 0) {
+    LOG_ERROR("unable to open dir '%s', error: %s", directory_path.c_str(), strerror(errno));
+    return false;
+  }
+
+  FILE* fp = std::fopen(temp_path.c_str(), "wt");
+  if (!fp) {
+    LOG_ERROR("unable to write to file '%s', error: %s", temp_path.c_str(), strerror(errno));
+    HandleError(temp_path, &dir_fd, &fp);
+    return false;
+  }
+
+  if (std::fprintf(fp, "%s", data.c_str()) < 0) {
+    LOG_ERROR("unable to write to file '%s', error: %s", temp_path.c_str(), strerror(errno));
+    HandleError(temp_path, &dir_fd, &fp);
+    return false;
+  }
+
+  // Flush the stream buffer to the temp file.
+  if (std::fflush(fp) != 0) {
+    LOG_ERROR("unable to write flush buffer to file '%s', error: %s", temp_path.c_str(), strerror(errno));
+    HandleError(temp_path, &dir_fd, &fp);
+    return false;
+  }
+
+  // Sync written temp file out to disk. fsync() is blocking until data makes it
+  // to disk.
+  if (fsync(fileno(fp)) != 0) {
+    LOG_WARN("unable to fsync file '%s', error: %s", temp_path.c_str(), strerror(errno));
+    // Allow fsync to fail and continue
+  }
+
+  if (std::fclose(fp) != 0) {
+    LOG_ERROR("unable to close file '%s', error: %s", temp_path.c_str(), strerror(errno));
+    HandleError(temp_path, &dir_fd, &fp);
+    return false;
+  }
+  fp = nullptr;
+
+  // Change the file's permissions to Read/Write by User and Group
+  if (chmod(temp_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0) {
+    LOG_ERROR("unable to change file permissions '%s', error: %s", temp_path.c_str(), strerror(errno));
+    HandleError(temp_path, &dir_fd, &fp);
+    return false;
+  }
+
+  // Rename written temp file to the actual config file.
+  if (std::rename(temp_path.c_str(), path.c_str()) != 0) {
+    LOG_ERROR("unable to commit file from '%s' to '%s', error: %s", temp_path.c_str(), path.c_str(), strerror(errno));
+    HandleError(temp_path, &dir_fd, &fp);
+    return false;
+  }
+
+  // This should ensure the directory is updated as well.
+  if (fsync(dir_fd) != 0) {
+    LOG_WARN("unable to fsync dir '%s', error: %s", directory_path.c_str(), strerror(errno));
+  }
+
+  if (close(dir_fd) != 0) {
+    LOG_ERROR("unable to close dir '%s', error: %s", directory_path.c_str(), strerror(errno));
+    HandleError(temp_path, &dir_fd, &fp);
+    return false;
+  }
+  return true;
+}
+
+}  // namespace os
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/os/linux_generic/files_test.cc b/gd/os/linux_generic/files_test.cc
new file mode 100644
index 0000000..383083c
--- /dev/null
+++ b/gd/os/linux_generic/files_test.cc
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#include "os/files.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <filesystem>
+
+namespace testing {
+
+using bluetooth::os::FileExists;
+using bluetooth::os::ReadSmallFile;
+using bluetooth::os::RenameFile;
+using bluetooth::os::WriteToFile;
+
+TEST(FilesTest, exist_test) {
+  auto temp_dir = std::filesystem::temp_directory_path();
+  auto temp_file = temp_dir / "file_1.txt";
+  std::string text = "Hello world!\n";
+  ASSERT_TRUE(WriteToFile(temp_file.string(), text));
+  EXPECT_TRUE(FileExists(temp_file.string()));
+  auto none_file = temp_dir / "file_nope.txt";
+  EXPECT_FALSE(FileExists(none_file.string()));
+}
+
+TEST(FilesTest, rename_test) {
+  auto temp_dir = std::filesystem::temp_directory_path();
+  auto temp_file = temp_dir / "file_1.txt";
+  std::string text = "Hello world!\n";
+  ASSERT_TRUE(WriteToFile(temp_file.string(), text));
+  EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text)));
+  auto to_file = temp_dir / "file_2.txt";
+  ASSERT_TRUE(RenameFile(temp_file.string(), to_file.string()));
+  EXPECT_FALSE(std::filesystem::exists(temp_file));
+  EXPECT_THAT(ReadSmallFile(to_file.string()), Optional(StrEq(text)));
+  // rename files that do not exist should return false
+  ASSERT_FALSE(RenameFile(temp_file.string(), to_file.string()));
+}
+
+TEST(FilesTest, write_read_loopback_test) {
+  auto temp_dir = std::filesystem::temp_directory_path();
+  auto temp_file = temp_dir / "file_1.txt";
+  std::string text = "Hello world!\n";
+  ASSERT_TRUE(WriteToFile(temp_file.string(), text));
+  EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text)));
+  EXPECT_TRUE(std::filesystem::remove(temp_file));
+}
+
+TEST(FilesTest, overwrite_test) {
+  auto temp_dir = std::filesystem::temp_directory_path();
+  auto temp_file = temp_dir / "file_1.txt";
+  std::string text = "Hello world!\n";
+  ASSERT_TRUE(WriteToFile(temp_file.string(), text));
+  EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text)));
+  text = "Foo bar!\n";
+  ASSERT_TRUE(WriteToFile(temp_file.string(), text));
+  EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text)));
+  EXPECT_TRUE(std::filesystem::remove(temp_file));
+}
+
+TEST(FilesTest, write_read_empty_string_test) {
+  auto temp_dir = std::filesystem::temp_directory_path();
+  auto temp_file = temp_dir / "file_1.txt";
+  std::string text;
+  ASSERT_TRUE(WriteToFile(temp_file.string(), text));
+  EXPECT_THAT(ReadSmallFile(temp_file.string()), Optional(StrEq(text)));
+  EXPECT_TRUE(std::filesystem::remove(temp_file));
+}
+
+TEST(FilesTest, read_non_existing_file_test) {
+  EXPECT_FALSE(ReadSmallFile("/woof"));
+}
+
+}  // namespace testing
\ No newline at end of file
diff --git a/gd/os/linux_generic/handler.cc b/gd/os/linux_generic/handler.cc
index 85cbcc3..2f996f9 100644
--- a/gd/os/linux_generic/handler.cc
+++ b/gd/os/linux_generic/handler.cc
@@ -18,6 +18,7 @@
 
 #include <sys/eventfd.h>
 #include <unistd.h>
+
 #include <cstring>
 
 #include "common/bind.h"
@@ -32,12 +33,13 @@
 
 namespace bluetooth {
 namespace os {
+using common::OnceClosure;
 
 Handler::Handler(Thread* thread)
     : tasks_(new std::queue<OnceClosure>()), thread_(thread), fd_(eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK)) {
   ASSERT(fd_ != -1);
-  reactable_ = thread_->GetReactor()->Register(fd_, common::Bind(&Handler::handle_next_event, common::Unretained(this)),
-                                               common::Closure());
+  reactable_ = thread_->GetReactor()->Register(
+      fd_, common::Bind(&Handler::handle_next_event, common::Unretained(this)), common::Closure());
 }
 
 Handler::~Handler() {
@@ -55,6 +57,7 @@
   {
     std::lock_guard<std::mutex> lock(mutex_);
     if (was_cleared()) {
+      LOG_WARN("Posting to a handler which has been cleared");
       return;
     }
     tasks_->emplace(std::move(closure));
diff --git a/gd/os/linux_generic/handler_unittest.cc b/gd/os/linux_generic/handler_unittest.cc
index 80456db..cea030e 100644
--- a/gd/os/linux_generic/handler_unittest.cc
+++ b/gd/os/linux_generic/handler_unittest.cc
@@ -17,6 +17,7 @@
 #include "os/handler.h"
 
 #include <sys/eventfd.h>
+
 #include <future>
 #include <thread>
 
@@ -52,12 +53,13 @@
   int val = 0;
   std::promise<void> closure_ran;
   auto future = closure_ran.get_future();
-  OnceClosure closure = common::BindOnce(
+  common::OnceClosure closure = common::BindOnce(
       [](int* val, std::promise<void> closure_ran) {
         *val = *val + 1;
         closure_ran.set_value();
       },
-      common::Unretained(&val), std::move(closure_ran));
+      common::Unretained(&val),
+      std::move(closure_ran));
   handler_->Post(std::move(closure));
   future.wait();
   ASSERT_EQ(val, 1);
@@ -76,7 +78,9 @@
         *val = *val + 1;
         can_continue_future.wait();
       },
-      common::Unretained(&val), std::move(closure_started), std::move(can_continue_future)));
+      common::Unretained(&val),
+      std::move(closure_started),
+      std::move(can_continue_future)));
   handler_->Post(common::BindOnce([]() { ASSERT_TRUE(false); }));
   closure_started_future.wait();
   handler_->Clear();
diff --git a/gd/os/linux_generic/linux.h b/gd/os/linux_generic/linux.h
index 524aa7d..69fae6a 100644
--- a/gd/os/linux_generic/linux.h
+++ b/gd/os/linux_generic/linux.h
@@ -19,3 +19,14 @@
 #ifndef EFD_SEMAPHORE
 #define EFD_SEMAPHORE 1
 #endif
+
+#ifdef FUZZ_TARGET
+#include "os/fuzz/fake_timerfd.h"
+#define TIMERFD_CREATE ::bluetooth::os::fuzz::fake_timerfd_create
+#define TIMERFD_SETTIME ::bluetooth::os::fuzz::fake_timerfd_settime
+#define TIMERFD_CLOSE ::bluetooth::os::fuzz::fake_timerfd_close
+#else
+#define TIMERFD_CREATE timerfd_create
+#define TIMERFD_SETTIME timerfd_settime
+#define TIMERFD_CLOSE close
+#endif
diff --git a/gd/os/linux_generic/queue.tpp b/gd/os/linux_generic/queue.tpp
index bd9a292..24113b1 100644
--- a/gd/os/linux_generic/queue.tpp
+++ b/gd/os/linux_generic/queue.tpp
@@ -19,8 +19,8 @@
 
 template <typename T>
 Queue<T>::~Queue() {
-  ASSERT(enqueue_.handler_ == nullptr);
-  ASSERT(dequeue_.handler_ == nullptr);
+  ASSERT_LOG(enqueue_.handler_ == nullptr, "Enqueue is not unregistered");
+  ASSERT_LOG(dequeue_.handler_ == nullptr, "Dequeue is not unregistered");
 };
 
 template <typename T>
@@ -37,11 +37,22 @@
 
 template <typename T>
 void Queue<T>::UnregisterEnqueue() {
-  std::lock_guard<std::mutex> lock(mutex_);
-  ASSERT(enqueue_.reactable_ != nullptr);
-  enqueue_.handler_->thread_->GetReactor()->Unregister(enqueue_.reactable_);
-  enqueue_.reactable_ = nullptr;
-  enqueue_.handler_ = nullptr;
+  Reactor* reactor = nullptr;
+  Reactor::Reactable* to_unregister = nullptr;
+  bool wait_for_unregister = false;
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(enqueue_.reactable_ != nullptr);
+    reactor = enqueue_.handler_->thread_->GetReactor();
+    wait_for_unregister = (!enqueue_.handler_->thread_->IsSameThread());
+    to_unregister = enqueue_.reactable_;
+    enqueue_.reactable_ = nullptr;
+    enqueue_.handler_ = nullptr;
+  }
+  reactor->Unregister(to_unregister);
+  if (wait_for_unregister) {
+    reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
+  }
 }
 
 template <typename T>
@@ -50,17 +61,28 @@
   ASSERT(dequeue_.handler_ == nullptr);
   ASSERT(dequeue_.reactable_ == nullptr);
   dequeue_.handler_ = handler;
-  dequeue_.reactable_ = dequeue_.handler_->thread_->GetReactor()->Register(dequeue_.reactive_semaphore_.GetFd(),
-                                                                           callback, base::Closure());
+  dequeue_.reactable_ = dequeue_.handler_->thread_->GetReactor()->Register(
+      dequeue_.reactive_semaphore_.GetFd(), callback, base::Closure());
 }
 
 template <typename T>
 void Queue<T>::UnregisterDequeue() {
-  std::lock_guard<std::mutex> lock(mutex_);
-  ASSERT(dequeue_.reactable_ != nullptr);
-  dequeue_.handler_->thread_->GetReactor()->Unregister(dequeue_.reactable_);
-  dequeue_.reactable_ = nullptr;
-  dequeue_.handler_ = nullptr;
+  Reactor* reactor = nullptr;
+  Reactor::Reactable* to_unregister = nullptr;
+  bool wait_for_unregister = false;
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(dequeue_.reactable_ != nullptr);
+    reactor = dequeue_.handler_->thread_->GetReactor();
+    wait_for_unregister = (!dequeue_.handler_->thread_->IsSameThread());
+    to_unregister = dequeue_.reactable_;
+    dequeue_.reactable_ = nullptr;
+    dequeue_.handler_ = nullptr;
+  }
+  reactor->Unregister(to_unregister);
+  if (wait_for_unregister) {
+    reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
+  }
 }
 
 template <typename T>
diff --git a/gd/os/linux_generic/queue_unittest.cc b/gd/os/linux_generic/queue_unittest.cc
index f0ca2bd..90f061c 100644
--- a/gd/os/linux_generic/queue_unittest.cc
+++ b/gd/os/linux_generic/queue_unittest.cc
@@ -17,6 +17,8 @@
 #include "os/queue.h"
 
 #include <sys/eventfd.h>
+
+#include <atomic>
 #include <future>
 #include <unordered_map>
 
@@ -359,8 +361,8 @@
 
   // Register dequeue and expect data move to dequeue end buffer
   std::unordered_map<int, std::promise<int>> dequeue_promise_map;
-  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
-                              std::forward_as_tuple());
+  dequeue_promise_map.emplace(
+      std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize), std::forward_as_tuple());
   auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
   test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
   dequeue_future.wait();
@@ -418,15 +420,15 @@
 
   // Register dequeue
   std::unordered_map<int, std::promise<int>> dequeue_promise_map;
-  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
-                              std::forward_as_tuple());
+  dequeue_promise_map.emplace(
+      std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize), std::forward_as_tuple());
   auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
   test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
 
   // Register enqueue
   std::unordered_map<int, std::promise<int>> enqueue_promise_map;
-  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
-                              std::forward_as_tuple());
+  enqueue_promise_map.emplace(
+      std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize), std::forward_as_tuple());
   auto enqueue_future = enqueue_promise_map[kHalfOfQueueSize].get_future();
   test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
 
@@ -603,8 +605,8 @@
 
   // Register dequeue, expect kHalfOfQueueSize data move to dequeue end buffer
   std::unordered_map<int, std::promise<int>> dequeue_promise_map;
-  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
-                              std::forward_as_tuple());
+  dequeue_promise_map.emplace(
+      std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize), std::forward_as_tuple());
   auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
   test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
   dequeue_future.wait();
@@ -688,21 +690,27 @@
   // Enqueue a string
   std::string valid = "Valid String";
   std::shared_ptr<std::string> shared = std::make_shared<std::string>(valid);
-  queue->RegisterEnqueue(enqueue_handler_, common::Bind(
-                                               [](Queue<std::string>* queue, std::shared_ptr<std::string> shared) {
-                                                 queue->UnregisterEnqueue();
-                                                 return std::make_unique<std::string>(*shared);
-                                               },
-                                               common::Unretained(queue), shared));
+  queue->RegisterEnqueue(
+      enqueue_handler_,
+      common::Bind(
+          [](Queue<std::string>* queue, std::shared_ptr<std::string> shared) {
+            queue->UnregisterEnqueue();
+            return std::make_unique<std::string>(*shared);
+          },
+          common::Unretained(queue),
+          shared));
 
   // Dequeue the string
-  queue->RegisterDequeue(dequeue_handler_, common::Bind(
-                                               [](Queue<std::string>* queue, std::string valid) {
-                                                 queue->UnregisterDequeue();
-                                                 auto answer = *queue->TryDequeue();
-                                                 ASSERT_EQ(answer, valid);
-                                               },
-                                               common::Unretained(queue), valid));
+  queue->RegisterDequeue(
+      dequeue_handler_,
+      common::Bind(
+          [](Queue<std::string>* queue, std::string valid) {
+            queue->UnregisterDequeue();
+            auto answer = *queue->TryDequeue();
+            ASSERT_EQ(answer, valid);
+          },
+          common::Unretained(queue),
+          valid));
 
   // Wait for both handlers to finish and delete the Queue
   std::promise<void> promise;
@@ -715,12 +723,83 @@
               delete queue;
               promise->set_value();
             },
-            common::Unretained(queue), common::Unretained(promise)));
+            common::Unretained(queue),
+            common::Unretained(promise)));
       },
-      common::Unretained(dequeue_handler_), common::Unretained(queue), common::Unretained(&promise)));
+      common::Unretained(dequeue_handler_),
+      common::Unretained(queue),
+      common::Unretained(&promise)));
   future.wait();
 }
 
+std::unique_ptr<std::string> sleep_and_enqueue_callback(int* to_increase) {
+  std::this_thread::sleep_for(std::chrono::milliseconds(100));
+  (*to_increase)++;
+  return std::make_unique<std::string>("Hello");
+}
+
+TEST_F(QueueTest, unregister_enqueue_and_wait) {
+  Queue<std::string> queue(10);
+  int* indicator = new int(100);
+  queue.RegisterEnqueue(enqueue_handler_, common::Bind(&sleep_and_enqueue_callback, common::Unretained(indicator)));
+  std::this_thread::sleep_for(std::chrono::milliseconds(50));
+  queue.UnregisterEnqueue();
+  EXPECT_EQ(*indicator, 101);
+  delete indicator;
+}
+
+std::unique_ptr<std::string> sleep_and_enqueue_callback_and_unregister(
+    int* to_increase, Queue<std::string>* queue, std::atomic_bool* is_registered) {
+  std::this_thread::sleep_for(std::chrono::milliseconds(100));
+  (*to_increase)++;
+  if (is_registered->exchange(false)) {
+    queue->UnregisterEnqueue();
+  }
+  return std::make_unique<std::string>("Hello");
+}
+
+TEST_F(QueueTest, unregister_enqueue_and_wait_maybe_unregistered) {
+  Queue<std::string> queue(10);
+  int* indicator = new int(100);
+  std::atomic_bool is_registered = true;
+  queue.RegisterEnqueue(
+      enqueue_handler_,
+      common::Bind(
+          &sleep_and_enqueue_callback_and_unregister,
+          common::Unretained(indicator),
+          common::Unretained(&queue),
+          common::Unretained(&is_registered)));
+  std::this_thread::sleep_for(std::chrono::milliseconds(50));
+  if (is_registered.exchange(false)) {
+    queue.UnregisterEnqueue();
+  }
+  EXPECT_EQ(*indicator, 101);
+  delete indicator;
+}
+
+void sleep_and_dequeue_callback(int* to_increase) {
+  std::this_thread::sleep_for(std::chrono::milliseconds(100));
+  (*to_increase)++;
+}
+
+TEST_F(QueueTest, unregister_dequeue_and_wait) {
+  int* indicator = new int(100);
+  Queue<std::string> queue(10);
+  queue.RegisterEnqueue(
+      enqueue_handler_,
+      common::Bind(
+          [](Queue<std::string>* queue) {
+            queue->UnregisterEnqueue();
+            return std::make_unique<std::string>("Hello");
+          },
+          common::Unretained(&queue)));
+  queue.RegisterDequeue(enqueue_handler_, common::Bind(&sleep_and_dequeue_callback, common::Unretained(indicator)));
+  std::this_thread::sleep_for(std::chrono::milliseconds(50));
+  queue.UnregisterDequeue();
+  EXPECT_EQ(*indicator, 101);
+  delete indicator;
+}
+
 // Create all threads for death tests in the function that dies
 class QueueDeathTest : public ::testing::Test {
  public:
@@ -728,8 +807,8 @@
     Thread* enqueue_thread = new Thread("enqueue_thread", Thread::Priority::NORMAL);
     Handler* enqueue_handler = new Handler(enqueue_thread);
     Queue<std::string>* queue = new Queue<std::string>(kQueueSizeOne);
-    queue->RegisterEnqueue(enqueue_handler,
-                           common::Bind([]() { return std::make_unique<std::string>("A string to fill the queue"); }));
+    queue->RegisterEnqueue(
+        enqueue_handler, common::Bind([]() { return std::make_unique<std::string>("A string to fill the queue"); }));
     delete queue;
   }
 
@@ -737,8 +816,9 @@
     Thread* dequeue_thread = new Thread("dequeue_thread", Thread::Priority::NORMAL);
     Handler* dequeue_handler = new Handler(dequeue_thread);
     Queue<std::string>* queue = new Queue<std::string>(kQueueSizeOne);
-    queue->RegisterDequeue(dequeue_handler, common::Bind([](Queue<std::string>* queue) { queue->TryDequeue(); },
-                                                         common::Unretained(queue)));
+    queue->RegisterDequeue(
+        dequeue_handler,
+        common::Bind([](Queue<std::string>* queue) { queue->TryDequeue(); }, common::Unretained(queue)));
     delete queue;
   }
 };
@@ -829,6 +909,18 @@
   ASSERT_FALSE(enqueue_.registered_);
 }
 
+TEST_F(EnqueueBufferTest, delete_when_in_callback) {
+  Queue<int>* queue = new Queue<int>(kQueueSize);
+  EnqueueBuffer<int>* enqueue_buffer = new EnqueueBuffer<int>(queue);
+  int num_items = 10;
+  for (int i = 0; i < num_items; i++) {
+    enqueue_buffer->Enqueue(std::make_unique<int>(i), handler_);
+  }
+
+  delete enqueue_buffer;
+  delete queue;
+}
+
 }  // namespace
 }  // namespace os
 }  // namespace bluetooth
diff --git a/gd/os/linux_generic/reactive_semaphore.cc b/gd/os/linux_generic/reactive_semaphore.cc
index df0050a..31c4afc 100644
--- a/gd/os/linux_generic/reactive_semaphore.cc
+++ b/gd/os/linux_generic/reactive_semaphore.cc
@@ -19,6 +19,7 @@
 #include <error.h>
 #include <sys/eventfd.h>
 #include <unistd.h>
+
 #include <functional>
 
 #include "os/linux_generic/linux.h"
diff --git a/gd/os/linux_generic/reactor.cc b/gd/os/linux_generic/reactor.cc
index 220d3a9..916c9e4 100644
--- a/gd/os/linux_generic/reactor.cc
+++ b/gd/os/linux_generic/reactor.cc
@@ -20,8 +20,10 @@
 #include <sys/epoll.h>
 #include <sys/eventfd.h>
 #include <unistd.h>
+
 #include <algorithm>
 #include <cerrno>
+#include <cinttypes>
 #include <cstring>
 
 #include "os/log.h"
@@ -30,17 +32,23 @@
 
 // Use at most sizeof(epoll_event) * kEpollMaxEvents kernel memory
 constexpr int kEpollMaxEvents = 64;
+constexpr uint64_t kStopReactor = 1 << 0;
+constexpr uint64_t kWaitForIdle = 1 << 1;
 
 }  // namespace
 
 namespace bluetooth {
 namespace os {
+using common::Closure;
 
 class Reactor::Reactable {
  public:
   Reactable(int fd, Closure on_read_ready, Closure on_write_ready)
-      : fd_(fd), on_read_ready_(std::move(on_read_ready)), on_write_ready_(std::move(on_write_ready)),
-        is_executing_(false), removed_(false) {}
+      : fd_(fd),
+        on_read_ready_(std::move(on_read_ready)),
+        on_write_ready_(std::move(on_write_ready)),
+        is_executing_(false),
+        removed_(false) {}
   const int fd_;
   Closure on_read_ready_;
   Closure on_write_ready_;
@@ -50,10 +58,7 @@
   std::unique_ptr<std::promise<void>> finished_promise_;
 };
 
-Reactor::Reactor()
-  : epoll_fd_(0),
-    control_fd_(0),
-    is_running_(false) {
+Reactor::Reactor() : epoll_fd_(0), control_fd_(0), is_running_(false) {
   RUN_NO_INTR(epoll_fd_ = epoll_create1(EPOLL_CLOEXEC));
   ASSERT_LOG(epoll_fd_ != -1, "could not create epoll fd: %s", strerror(errno));
 
@@ -82,6 +87,8 @@
   bool already_running = is_running_.exchange(true);
   ASSERT(!already_running);
 
+  int timeout_ms = -1;
+  bool waiting_for_idle = false;
   for (;;) {
     {
       std::unique_lock<std::mutex> lock(mutex_);
@@ -89,8 +96,14 @@
     }
     epoll_event events[kEpollMaxEvents];
     int count;
-    RUN_NO_INTR(count = epoll_wait(epoll_fd_, events, kEpollMaxEvents, -1));
+    RUN_NO_INTR(count = epoll_wait(epoll_fd_, events, kEpollMaxEvents, timeout_ms));
     ASSERT(count != -1);
+    if (waiting_for_idle && count == 0) {
+      timeout_ms = -1;
+      waiting_for_idle = false;
+      idle_promise_->set_value();
+      idle_promise_ = nullptr;
+    }
 
     for (int i = 0; i < count; ++i) {
       auto event = events[i];
@@ -100,11 +113,21 @@
       if (event.data.ptr == nullptr) {
         uint64_t value;
         eventfd_read(control_fd_, &value);
-        is_running_ = false;
-        return;
+        if ((value & kStopReactor) != 0) {
+          is_running_ = false;
+          return;
+        } else if ((value & kWaitForIdle) != 0) {
+          timeout_ms = 30;
+          waiting_for_idle = true;
+          continue;
+        } else {
+          LOG_ERROR("Unknown control_fd value %" PRIu64 "x", value);
+          continue;
+        }
       }
       auto* reactable = static_cast<Reactor::Reactable*>(event.data.ptr);
       std::unique_lock<std::mutex> lock(mutex_);
+      executing_reactable_finished_ = nullptr;
       // See if this reactable has been removed in the meantime.
       if (std::find(invalidation_list_.begin(), invalidation_list_.end(), reactable) != invalidation_list_.end()) {
         continue;
@@ -124,10 +147,10 @@
       {
         std::lock_guard<std::mutex> reactable_lock(reactable->mutex_);
         reactable->is_executing_ = false;
-      }
-      if (reactable->removed_) {
-        reactable->finished_promise_->set_value();
-        delete reactable;
+        if (reactable->removed_) {
+          reactable->finished_promise_->set_value();
+          delete reactable;
+        }
       }
     }
   }
@@ -137,7 +160,7 @@
   if (!is_running_) {
     LOG_WARN("not running, will stop once it's started");
   }
-  auto control = eventfd_write(control_fd_, 1);
+  auto control = eventfd_write(control_fd_, kStopReactor);
   ASSERT(control != -1);
 }
 
@@ -182,7 +205,7 @@
     if (reactable->is_executing_) {
       reactable->removed_ = true;
       reactable->finished_promise_ = std::make_unique<std::promise<void>>();
-      executing_reactable_finished_ = std::make_unique<std::future<void>>(reactable->finished_promise_->get_future());
+      executing_reactable_finished_ = std::make_shared<std::future<void>>(reactable->finished_promise_->get_future());
       delaying_delete_until_callback_finished = true;
     }
   }
@@ -193,13 +216,32 @@
 }
 
 bool Reactor::WaitForUnregisteredReactable(std::chrono::milliseconds timeout) {
+  std::lock_guard<std::mutex> lock(mutex_);
   if (executing_reactable_finished_ == nullptr) {
     return true;
   }
   auto stop_status = executing_reactable_finished_->wait_for(timeout);
+  if (stop_status != std::future_status::ready) {
+    LOG_ERROR("Unregister reactable timed out");
+  }
   return stop_status == std::future_status::ready;
 }
 
+bool Reactor::WaitForIdle(std::chrono::milliseconds timeout) {
+  auto promise = std::make_shared<std::promise<void>>();
+  auto future = std::make_unique<std::future<void>>(promise->get_future());
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    idle_promise_ = promise;
+  }
+
+  auto control = eventfd_write(control_fd_, kWaitForIdle);
+  ASSERT(control != -1);
+
+  auto idle_status = future->wait_for(timeout);
+  return idle_status == std::future_status::ready;
+}
+
 void Reactor::ModifyRegistration(Reactor::Reactable* reactable, Closure on_read_ready, Closure on_write_ready) {
   ASSERT(reactable != nullptr);
 
diff --git a/gd/os/linux_generic/reactor_unittest.cc b/gd/os/linux_generic/reactor_unittest.cc
index b9cd07d..dd7c7a9 100644
--- a/gd/os/linux_generic/reactor_unittest.cc
+++ b/gd/os/linux_generic/reactor_unittest.cc
@@ -17,6 +17,7 @@
 #include "os/reactor.h"
 
 #include <sys/eventfd.h>
+
 #include <chrono>
 #include <future>
 #include <thread>
@@ -100,9 +101,10 @@
       g_promise->set_value(kReadReadyValue);
     }
     if (value == kRegisterSampleReactable) {
-      reactable_ =
-          reactor_->Register(sample_reactable_.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(this)),
-                             Bind(&FakeReactable::OnWriteReadyNoOp, common::Unretained(this)));
+      reactable_ = reactor_->Register(
+          sample_reactable_.fd_,
+          Bind(&FakeReactable::OnReadReady, common::Unretained(this)),
+          Bind(&FakeReactable::OnWriteReadyNoOp, common::Unretained(this)));
       g_promise->set_value(kReadReadyValue);
     }
     if (value == kUnregisterSampleReactable) {
@@ -187,7 +189,7 @@
 TEST_F(ReactorTest, cold_register_only) {
   FakeReactable fake_reactable;
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
 
   reactor_->Unregister(reactable);
 }
@@ -195,7 +197,7 @@
 TEST_F(ReactorTest, cold_register) {
   FakeReactable fake_reactable;
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   auto future = g_promise->get_future();
 
@@ -213,7 +215,7 @@
 
   FakeReactable fake_reactable;
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
   auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kSetPromise);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
@@ -226,7 +228,9 @@
 TEST_F(ReactorTest, unregister_from_different_thread_while_task_is_executing_) {
   FakeRunningReactable fake_reactable;
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
+      fake_reactable.fd_,
+      Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
+      common::Closure());
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   auto write_result = eventfd_write(fake_reactable.fd_, 1);
   ASSERT_EQ(write_result, 0);
@@ -242,7 +246,8 @@
 TEST_F(ReactorTest, unregister_from_different_thread_while_task_is_executing_wait_fails) {
   FakeRunningReactable fake_reactable;
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, common::Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
+      fake_reactable.fd_,
+      common::Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
       common::Closure());
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   auto write_result = eventfd_write(fake_reactable.fd_, 1);
@@ -260,7 +265,8 @@
 TEST_F(ReactorTest, unregister_from_different_thread_while_task_is_executing_wait_succeeds) {
   FakeRunningReactable fake_reactable;
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, common::Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
+      fake_reactable.fd_,
+      common::Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
       common::Closure());
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   auto write_result = eventfd_write(fake_reactable.fd_, 1);
@@ -278,7 +284,7 @@
 TEST_F(ReactorTest, hot_unregister_from_different_thread) {
   FakeReactable fake_reactable;
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   reactor_->Unregister(reactable);
   auto future = g_promise->get_future();
@@ -298,7 +304,7 @@
 
   FakeReactable fake_reactable(reactor_);
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
   auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
@@ -319,7 +325,7 @@
 
   FakeReactable fake_reactable(reactor_);
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
   auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
@@ -342,11 +348,13 @@
 
   FakeReactable fake_reactable1(reactor_);
   auto* reactable1 = reactor_->Register(
-      fake_reactable1.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable1)), Closure());
+      fake_reactable1.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable1)), common::Closure());
 
   FakeReactable fake_reactable2(reactor_);
   auto* reactable2 = reactor_->Register(
-      fake_reactable2.fd_, Bind(&FakeReactable::UnregisterInCallback, common::Unretained(&fake_reactable2)), Closure());
+      fake_reactable2.fd_,
+      Bind(&FakeReactable::UnregisterInCallback, common::Unretained(&fake_reactable2)),
+      common::Closure());
   fake_reactable2.reactable_ = reactable2;
   auto write_result = eventfd_write(fake_reactable2.fd_, 1);
   EXPECT_EQ(write_result, 0);
@@ -362,11 +370,13 @@
 
   FakeReactable fake_reactable1(reactor_);
   auto* reactable1 = reactor_->Register(
-      fake_reactable1.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable1)), Closure());
+      fake_reactable1.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable1)), common::Closure());
 
   FakeReactable fake_reactable2(reactor_);
   auto* reactable2 = reactor_->Register(
-      fake_reactable2.fd_, Bind(&FakeReactable::UnregisterInCallback, common::Unretained(&fake_reactable2)), Closure());
+      fake_reactable2.fd_,
+      Bind(&FakeReactable::UnregisterInCallback, common::Unretained(&fake_reactable2)),
+      common::Closure());
   fake_reactable2.reactable_ = reactable2;
   auto write_result = eventfd_write(fake_reactable2.fd_, 1);
   EXPECT_EQ(write_result, 0);
@@ -390,8 +400,8 @@
 
 TEST_F(ReactorTest, on_write_ready) {
   FakeReactable fake_reactable;
-  auto* reactable = reactor_->Register(fake_reactable.fd_, Closure(),
-                                       Bind(&FakeReactable::OnWriteReady, common::Unretained(&fake_reactable)));
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, common::Closure(), Bind(&FakeReactable::OnWriteReady, common::Unretained(&fake_reactable)));
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   uint64_t value = 0;
   auto read_result = eventfd_read(fake_reactable.fd_, &value);
@@ -407,9 +417,9 @@
 TEST_F(ReactorTest, modify_registration) {
   FakeReactable fake_reactable;
   auto* reactable = reactor_->Register(
-      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
-  reactor_->ModifyRegistration(reactable, Closure(),
-                               Bind(&FakeReactable::OnWriteReady, common::Unretained(&fake_reactable)));
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
+  reactor_->ModifyRegistration(
+      reactable, common::Closure(), Bind(&FakeReactable::OnWriteReady, common::Unretained(&fake_reactable)));
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   uint64_t value = 0;
   auto read_result = eventfd_read(fake_reactable.fd_, &value);
diff --git a/gd/os/linux_generic/repeating_alarm.cc b/gd/os/linux_generic/repeating_alarm.cc
index 79cda6d..8a718f2 100644
--- a/gd/os/linux_generic/repeating_alarm.cc
+++ b/gd/os/linux_generic/repeating_alarm.cc
@@ -17,10 +17,12 @@
 #include "os/repeating_alarm.h"
 
 #include <sys/timerfd.h>
-#include <cstring>
 #include <unistd.h>
 
+#include <cstring>
+
 #include "common/bind.h"
+#include "os/linux_generic/linux.h"
 #include "os/log.h"
 #include "os/utils.h"
 
@@ -32,8 +34,9 @@
 
 namespace bluetooth {
 namespace os {
+using common::Closure;
 
-RepeatingAlarm::RepeatingAlarm(Handler* handler) : handler_(handler), fd_(timerfd_create(ALARM_CLOCK, 0)) {
+RepeatingAlarm::RepeatingAlarm(Handler* handler) : handler_(handler), fd_(TIMERFD_CREATE(ALARM_CLOCK, 0)) {
   ASSERT(fd_ != -1);
 
   token_ = handler_->thread_->GetReactor()->Register(
@@ -44,18 +47,16 @@
   handler_->thread_->GetReactor()->Unregister(token_);
 
   int close_status;
-  RUN_NO_INTR(close_status = close(fd_));
+  RUN_NO_INTR(close_status = TIMERFD_CLOSE(fd_));
   ASSERT(close_status != -1);
 }
 
 void RepeatingAlarm::Schedule(Closure task, std::chrono::milliseconds period) {
   std::lock_guard<std::mutex> lock(mutex_);
   long period_ms = period.count();
-  itimerspec timer_itimerspec{
-    {period_ms / 1000, period_ms % 1000 * 1000000},
-    {period_ms / 1000, period_ms % 1000 * 1000000}
-  };
-  int result = timerfd_settime(fd_, 0, &timer_itimerspec, nullptr);
+  itimerspec timer_itimerspec{{period_ms / 1000, period_ms % 1000 * 1000000},
+                              {period_ms / 1000, period_ms % 1000 * 1000000}};
+  int result = TIMERFD_SETTIME(fd_, 0, &timer_itimerspec, nullptr);
   ASSERT(result == 0);
 
   task_ = std::move(task);
@@ -64,7 +65,7 @@
 void RepeatingAlarm::Cancel() {
   std::lock_guard<std::mutex> lock(mutex_);
   itimerspec disarm_itimerspec{/* disarm timer */};
-  int result = timerfd_settime(fd_, 0, &disarm_itimerspec, nullptr);
+  int result = TIMERFD_SETTIME(fd_, 0, &disarm_itimerspec, nullptr);
   ASSERT(result == 0);
 }
 
diff --git a/gd/os/linux_generic/repeating_alarm_unittest.cc b/gd/os/linux_generic/repeating_alarm_unittest.cc
index d30bbd9..c7e1022 100644
--- a/gd/os/linux_generic/repeating_alarm_unittest.cc
+++ b/gd/os/linux_generic/repeating_alarm_unittest.cc
@@ -47,16 +47,28 @@
     auto future = promise.get_future();
     auto start_time = std::chrono::steady_clock::now();
     int counter = 0;
-    alarm_->Schedule(common::Bind(&RepeatingAlarmTest::verify_delayed_tasks, common::Unretained(this),
-                                  common::Unretained(&counter), start_time, scheduled_tasks,
-                                  common::Unretained(&promise), task_length_ms, interval_between_tasks_ms),
-                     std::chrono::milliseconds(interval_between_tasks_ms));
+    alarm_->Schedule(
+        common::Bind(
+            &RepeatingAlarmTest::verify_delayed_tasks,
+            common::Unretained(this),
+            common::Unretained(&counter),
+            start_time,
+            scheduled_tasks,
+            common::Unretained(&promise),
+            task_length_ms,
+            interval_between_tasks_ms),
+        std::chrono::milliseconds(interval_between_tasks_ms));
     future.get();
     alarm_->Cancel();
   }
 
-  void verify_delayed_tasks(int* counter, std::chrono::steady_clock::time_point start_time, int scheduled_tasks,
-                            std::promise<void>* promise, int task_length_ms, int interval_between_tasks_ms) {
+  void verify_delayed_tasks(
+      int* counter,
+      std::chrono::steady_clock::time_point start_time,
+      int scheduled_tasks,
+      std::promise<void>* promise,
+      int task_length_ms,
+      int interval_between_tasks_ms) {
     *counter = *counter + 1;
     auto time_now = std::chrono::steady_clock::now();
     auto time_delta = time_now - start_time;
@@ -69,7 +81,7 @@
 
   RepeatingAlarm* alarm_;
 
-  Closure should_not_happen_ = common::Bind([] { ASSERT_TRUE(false); });
+  common::Closure should_not_happen_ = common::Bind([] { ASSERT_TRUE(false); });
 
  private:
   Thread* thread_;
@@ -85,8 +97,8 @@
   auto future = promise.get_future();
   auto before = std::chrono::steady_clock::now();
   int period_ms = 10;
-  alarm_->Schedule(common::Bind(&std::promise<void>::set_value, common::Unretained(&promise)),
-                   std::chrono::milliseconds(period_ms));
+  alarm_->Schedule(
+      common::Bind(&std::promise<void>::set_value, common::Unretained(&promise)), std::chrono::milliseconds(period_ms));
   future.get();
   alarm_->Cancel();
   auto after = std::chrono::steady_clock::now();
@@ -95,14 +107,14 @@
 }
 
 TEST_F(RepeatingAlarmTest, cancel_alarm) {
-  alarm_->Schedule(should_not_happen_, std::chrono::milliseconds(1));
+  alarm_->Schedule(should_not_happen_, std::chrono::milliseconds(10));
   alarm_->Cancel();
-  std::this_thread::sleep_for(std::chrono::milliseconds(5));
+  std::this_thread::sleep_for(std::chrono::milliseconds(50));
 }
 
 TEST_F(RepeatingAlarmTest, cancel_alarm_from_callback) {
-  alarm_->Schedule(common::Bind(&RepeatingAlarm::Cancel, common::Unretained(this->alarm_)),
-                   std::chrono::milliseconds(1));
+  alarm_->Schedule(
+      common::Bind(&RepeatingAlarm::Cancel, common::Unretained(this->alarm_)), std::chrono::milliseconds(1));
   std::this_thread::sleep_for(std::chrono::milliseconds(5));
 }
 
@@ -110,8 +122,8 @@
   alarm_->Schedule(should_not_happen_, std::chrono::milliseconds(1));
   std::promise<void> promise;
   auto future = promise.get_future();
-  alarm_->Schedule(common::Bind(&std::promise<void>::set_value, common::Unretained(&promise)),
-                   std::chrono::milliseconds(10));
+  alarm_->Schedule(
+      common::Bind(&std::promise<void>::set_value, common::Unretained(&promise)), std::chrono::milliseconds(10));
   future.get();
   alarm_->Cancel();
 }
diff --git a/gd/os/linux_generic/thread.cc b/gd/os/linux_generic/thread.cc
index c2969fe..e1c1a34 100644
--- a/gd/os/linux_generic/thread.cc
+++ b/gd/os/linux_generic/thread.cc
@@ -17,8 +17,9 @@
 #include "os/thread.h"
 
 #include <fcntl.h>
-#include <unistd.h>
 #include <sys/syscall.h>
+#include <unistd.h>
+
 #include <cerrno>
 #include <cstring>
 
@@ -32,9 +33,7 @@
 }
 
 Thread::Thread(const std::string& name, const Priority priority)
-    : name_(name),
-      reactor_(),
-      running_thread_(&Thread::run, this, priority) {}
+    : name_(name), reactor_(), running_thread_(&Thread::run, this, priority) {}
 
 void Thread::run(Priority priority) {
   if (priority == Priority::REAL_TIME) {
diff --git a/gd/os/linux_generic/thread_unittest.cc b/gd/os/linux_generic/thread_unittest.cc
index f0b8f24..811b6c2 100644
--- a/gd/os/linux_generic/thread_unittest.cc
+++ b/gd/os/linux_generic/thread_unittest.cc
@@ -85,9 +85,10 @@
 TEST_F(ThreadTest, same_thread) {
   Reactor* reactor = thread->GetReactor();
   SampleReactable sample_reactable(thread);
-  auto* reactable =
-      reactor->Register(sample_reactable.fd_,
-                        common::Bind(&SampleReactable::OnReadReady, common::Unretained(&sample_reactable)), Closure());
+  auto* reactable = reactor->Register(
+      sample_reactable.fd_,
+      common::Bind(&SampleReactable::OnReadReady, common::Unretained(&sample_reactable)),
+      common::Closure());
   int fd = sample_reactable.fd_;
   int write_result = eventfd_write(fd, kCheckIsSameThread);
   EXPECT_EQ(write_result, 0);
diff --git a/gd/os/log.h b/gd/os/log.h
index 195e1d6..ccbadbb 100644
--- a/gd/os/log.h
+++ b/gd/os/log.h
@@ -28,10 +28,17 @@
 
 #include <log/log.h>
 
+#ifdef FUZZ_TARGET
+#define LOG_VERBOSE(...)
+#define LOG_DEBUG(...)
+#define LOG_INFO(...)
+#define LOG_WARN(...)
+#else
 #define LOG_VERBOSE(fmt, args...) ALOGV("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
 #define LOG_DEBUG(fmt, args...) ALOGD("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
 #define LOG_INFO(fmt, args...) ALOGI("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
 #define LOG_WARN(fmt, args...) ALOGW("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
+#endif /* FUZZ_TARGET */
 #define LOG_ERROR(fmt, args...) ALOGE("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
 
 #else
@@ -53,16 +60,25 @@
     fprintf(stderr, "%s %s - %s:%d - %s: " fmt "\n", buf, LOG_TAG, __FILE__, __LINE__, __func__, ##args);             \
   } while (false)
 
+#ifdef FUZZ_TARGET
+#define LOG_VERBOSE(...)
+#define LOG_DEBUG(...)
+#define LOG_INFO(...)
+#define LOG_WARN(...)
+#else
 #define LOG_VERBOSE(...) LOGWRAPPER(__VA_ARGS__)
 #define LOG_DEBUG(...) LOGWRAPPER(__VA_ARGS__)
 #define LOG_INFO(...) LOGWRAPPER(__VA_ARGS__)
 #define LOG_WARN(...) LOGWRAPPER(__VA_ARGS__)
+#endif /* FUZZ_TARGET */
 #define LOG_ERROR(...) LOGWRAPPER(__VA_ARGS__)
 #define LOG_ALWAYS_FATAL(...) \
   do {                        \
     LOGWRAPPER(__VA_ARGS__);  \
     abort();                  \
   } while (false)
+#define android_errorWriteLog(tag, subTag) LOG_ERROR("ERROR tag: 0x%x, sub_tag: %s", tag, subTag)
+#define LOG_EVENT_INT(...)
 
 #endif /* defined(OS_ANDROID) */
 
diff --git a/gd/l2cap/security_policy.h b/gd/os/parameter_provider.h
similarity index 72%
copy from gd/l2cap/security_policy.h
copy to gd/os/parameter_provider.h
index 5a06401..b97964c 100644
--- a/gd/l2cap/security_policy.h
+++ b/gd/os/parameter_provider.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,12 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
+#include <string>
+
 namespace bluetooth {
-namespace l2cap {
+namespace os {
 
-class SecurityPolicy {};
+class ParameterProvider {
+ public:
+  // Return the path to config file for storage module
+  static std::string ConfigFilePath();
+};
 
-}  // namespace l2cap
+}  // namespace os
 }  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/os/queue.h b/gd/os/queue.h
index 1d2af02..cd8b554 100644
--- a/gd/os/queue.h
+++ b/gd/os/queue.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <unistd.h>
+
 #include <functional>
 #include <mutex>
 #include <queue>
@@ -36,7 +37,7 @@
 template <typename T>
 class IQueueEnqueue {
  public:
-  using EnqueueCallback = Callback<std::unique_ptr<T>()>;
+  using EnqueueCallback = common::Callback<std::unique_ptr<T>()>;
   virtual ~IQueueEnqueue() = default;
   virtual void RegisterEnqueue(Handler* handler, EnqueueCallback callback) = 0;
   virtual void UnregisterEnqueue() = 0;
@@ -46,7 +47,7 @@
 template <typename T>
 class IQueueDequeue {
  public:
-  using DequeueCallback = Callback<void()>;
+  using DequeueCallback = common::Callback<void()>;
   virtual ~IQueueDequeue() = default;
   virtual void RegisterDequeue(Handler* handler, DequeueCallback callback) = 0;
   virtual void UnregisterDequeue() = 0;
@@ -58,10 +59,10 @@
  public:
   // A function moving data from enqueue end buffer to queue, it will be continually be invoked until queue
   // is full. Enqueue end should make sure buffer isn't empty and UnregisterEnqueue when buffer become empty.
-  using EnqueueCallback = Callback<std::unique_ptr<T>()>;
+  using EnqueueCallback = common::Callback<std::unique_ptr<T>()>;
   // A function moving data form queue to dequeue end buffer, it will be continually be invoked until queue
   // is empty. TryDequeue should be use in this function to get data from queue.
-  using DequeueCallback = Callback<void()>;
+  using DequeueCallback = common::Callback<void()>;
   // Create a queue with |capacity| is the maximum number of messages a queue can contain
   explicit Queue(size_t capacity);
   ~Queue();
@@ -106,37 +107,58 @@
  public:
   EnqueueBuffer(IQueueEnqueue<T>* queue) : queue_(queue) {}
 
+  ~EnqueueBuffer() {
+    if (enqueue_registered_.exchange(false)) {
+      queue_->UnregisterEnqueue();
+    }
+  }
+
   void Enqueue(std::unique_ptr<T> t, os::Handler* handler) {
     std::lock_guard<std::mutex> lock(mutex_);
     buffer_.push(std::move(t));
-    if (buffer_.size() == 1) {
+    if (!enqueue_registered_.exchange(true)) {
       queue_->RegisterEnqueue(handler, common::Bind(&EnqueueBuffer<T>::enqueue_callback, common::Unretained(this)));
     }
   }
 
   void Clear() {
     std::lock_guard<std::mutex> lock(mutex_);
-    if (!buffer_.empty()) {
+    if (enqueue_registered_.exchange(false)) {
       queue_->UnregisterEnqueue();
       std::queue<std::unique_ptr<T>> empty;
       std::swap(buffer_, empty);
     }
   }
 
+  auto Size() const {
+    return buffer_.size();
+  }
+
+  void NotifyOnEmpty(common::OnceClosure callback) {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(callback_on_empty_.is_null());
+    callback_on_empty_ = std::move(callback);
+  }
+
  private:
   std::unique_ptr<T> enqueue_callback() {
     std::lock_guard<std::mutex> lock(mutex_);
     std::unique_ptr<T> enqueued_t = std::move(buffer_.front());
     buffer_.pop();
-    if (buffer_.empty()) {
+    if (buffer_.empty() && enqueue_registered_.exchange(false)) {
       queue_->UnregisterEnqueue();
+      if (!callback_on_empty_.is_null()) {
+        std::move(callback_on_empty_).Run();
+      }
     }
     return enqueued_t;
   }
 
   mutable std::mutex mutex_;
   IQueueEnqueue<T>* queue_;
+  std::atomic_bool enqueue_registered_ = false;
   std::queue<std::unique_ptr<T>> buffer_;
+  common::OnceClosure callback_on_empty_;
 };
 
 #ifdef OS_LINUX_GENERIC
diff --git a/gd/os/queue_benchmark.cc b/gd/os/queue_benchmark.cc
index 0c90fe9..2066aea 100644
--- a/gd/os/queue_benchmark.cc
+++ b/gd/os/queue_benchmark.cc
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-#include "benchmark/benchmark.h"
-
 #include <future>
 
+#include "benchmark/benchmark.h"
 #include "os/handler.h"
 #include "os/queue.h"
 #include "os/thread.h"
diff --git a/gd/os/rand.h b/gd/os/rand.h
new file mode 100644
index 0000000..3c38c74
--- /dev/null
+++ b/gd/os/rand.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <openssl/rand.h>
+
+#include <array>
+
+namespace bluetooth {
+namespace os {
+
+template <size_t SIZE>
+std::array<uint8_t, SIZE> GenerateRandom() {
+  std::array<uint8_t, SIZE> ret;
+  ASSERT(RAND_bytes(ret.data(), ret.size()) == 1);
+  return ret;
+}
+
+inline uint32_t GenerateRandom() {
+  uint32_t ret{};
+  ASSERT(RAND_bytes((uint8_t*)(&ret), sizeof(uint32_t)) == 1);
+  return ret;
+}
+
+}  // namespace os
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/os/reactor.h b/gd/os/reactor.h
index 7e8476b..e61f4f5 100644
--- a/gd/os/reactor.h
+++ b/gd/os/reactor.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <sys/epoll.h>
+
 #include <atomic>
 #include <functional>
 #include <future>
@@ -30,11 +31,6 @@
 namespace bluetooth {
 namespace os {
 
-using common::Callback;
-using common::Closure;
-using common::OnceCallback;
-using common::OnceClosure;
-
 // A simple implementation of reactor-style looper.
 // When a reactor is running, the main loop is polling and blocked until at least one registered reactable is ready to
 // read or write. It will invoke on_read_ready() or on_write_ready(), which is registered with the reactor. Then, it
@@ -61,7 +57,7 @@
 
   // Register a reactable fd to this reactor. Returns a pointer to a Reactable. Caller must use this object to
   // unregister or modify registration. Ownership of the memory space is NOT transferred to user.
-  Reactable* Register(int fd, Closure on_read_ready, Closure on_write_ready);
+  Reactable* Register(int fd, common::Closure on_read_ready, common::Closure on_write_ready);
 
   // Unregister a reactable from this reactor
   void Unregister(Reactable* reactable);
@@ -69,8 +65,11 @@
   // Wait for up to timeout milliseconds, and return true if the reactable finished executing.
   bool WaitForUnregisteredReactable(std::chrono::milliseconds timeout);
 
+  // Wait for up to timeout milliseconds, and return true if we reached idle.
+  bool WaitForIdle(std::chrono::milliseconds timeout);
+
   // Modify the registration for a reactable with given reactable
-  void ModifyRegistration(Reactable* reactable, Closure on_read_ready, Closure on_write_ready);
+  void ModifyRegistration(Reactable* reactable, common::Closure on_read_ready, common::Closure on_write_ready);
 
  private:
   mutable std::mutex mutex_;
@@ -78,7 +77,8 @@
   int control_fd_;
   std::atomic<bool> is_running_;
   std::list<Reactable*> invalidation_list_;
-  std::unique_ptr<std::future<void>> executing_reactable_finished_;
+  std::shared_ptr<std::future<void>> executing_reactable_finished_;
+  std::shared_ptr<std::promise<void>> idle_promise_;
 };
 
 }  // namespace os
diff --git a/gd/os/repeating_alarm.h b/gd/os/repeating_alarm.h
index cd91344..6b37162 100644
--- a/gd/os/repeating_alarm.h
+++ b/gd/os/repeating_alarm.h
@@ -42,13 +42,13 @@
   DISALLOW_COPY_AND_ASSIGN(RepeatingAlarm);
 
   // Schedule a repeating alarm with given period
-  void Schedule(Closure task, std::chrono::milliseconds period);
+  void Schedule(common::Closure task, std::chrono::milliseconds period);
 
   // Cancel the alarm. No-op if it's not armed.
   void Cancel();
 
  private:
-  Closure task_;
+  common::Closure task_;
   Handler* handler_;
   int fd_ = 0;
   Reactor::Reactable* token_;
diff --git a/gd/os/system_properties.h b/gd/os/system_properties.h
new file mode 100644
index 0000000..0d464b6
--- /dev/null
+++ b/gd/os/system_properties.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+
+namespace bluetooth {
+namespace os {
+
+// Get |property| keyed system property from supported platform, return std::nullopt if the property does not exist
+// or if the platform does not support system property
+std::optional<std::string> GetSystemProperty(const std::string& property);
+
+// Set |property| keyed system property to |value|, return true if the set was successful and false if the set failed
+// Replace existing value if property already exists
+bool SetSystemProperty(const std::string& property, const std::string& value);
+
+}  // namespace os
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/os/thread_benchmark.cc b/gd/os/thread_benchmark.cc
index 8d62889c..7a0e140 100644
--- a/gd/os/thread_benchmark.cc
+++ b/gd/os/thread_benchmark.cc
@@ -19,7 +19,6 @@
 #include <thread>
 
 #include "benchmark/benchmark.h"
-
 #include "common/bind.h"
 #include "os/handler.h"
 #include "os/thread.h"
@@ -81,8 +80,8 @@
     counter_promise_ = std::promise<void>();
     std::future<void> counter_future = counter_promise_.get_future();
     for (int i = 0; i < num_messages_to_send_; i++) {
-      handler_->Post(BindOnce(&BM_ReactorThread_batch_enque_dequeue_Benchmark::callback_batch,
-                              bluetooth::common::Unretained(this)));
+      handler_->Post(BindOnce(
+          &BM_ReactorThread_batch_enque_dequeue_Benchmark::callback_batch, bluetooth::common::Unretained(this)));
     }
     counter_future.wait();
   }
diff --git a/gd/packet/iterator.h b/gd/packet/iterator.h
index 8d927a0..6aaee66 100644
--- a/gd/packet/iterator.h
+++ b/gd/packet/iterator.h
@@ -67,7 +67,7 @@
   template <typename FixedWidthPODType>
   FixedWidthPODType extract() {
     static_assert(std::is_pod<FixedWidthPODType>::value, "Iterator::extract requires a fixed-width type.");
-    FixedWidthPODType extracted_value;
+    FixedWidthPODType extracted_value{};
     uint8_t* value_ptr = (uint8_t*)&extracted_value;
 
     for (size_t i = 0; i < sizeof(FixedWidthPODType); i++) {
diff --git a/gd/packet/parser/README b/gd/packet/parser/README
index 9006c94..6915538 100644
--- a/gd/packet/parser/README
+++ b/gd/packet/parser/README
@@ -14,11 +14,11 @@
 
 Checksum types
   checksum MyChecksumClass : 16 "path/to/the/class/"
-  Checksum fields need to implement the following three static methods:
-    static void Initialize(MyChecksumClass&);
-    static void AddByte(MyChecksumClass&, uint8_t);
+  Checksum fields need to implement the following three methods:
+    void Initialize(MyChecksumClass&);
+    void AddByte(MyChecksumClass&, uint8_t);
     // Assuming a 16-bit (uint16_t) checksum:
-    static uint16_t GetChecksum(MyChecksumClass&);
+    uint16_t GetChecksum(MyChecksumClass&);
 -------------
  LIMITATIONS
 -------------
@@ -57,3 +57,4 @@
   static void Serialize(const Type&, MutableView&);
   static std::optional<size_t> Size(Iterator);
   static Type Parse(Iterator);
+  std::string ToString();
\ No newline at end of file
diff --git a/gd/packet/parser/custom_field_def.cc b/gd/packet/parser/custom_field_def.cc
index 3f2076d..fb027c2 100644
--- a/gd/packet/parser/custom_field_def.cc
+++ b/gd/packet/parser/custom_field_def.cc
@@ -43,6 +43,10 @@
   s << "#include \"" << include_ << util::CamelCaseToUnderScore(GetTypeName()) << ".h\"\n";
 }
 
+void CustomFieldDef::GenPyBind11Include(std::ostream& s) const {
+  s << "#include \"" << include_ << util::CamelCaseToUnderScore(GetTypeName()) << "_pybind11_type_caster.h\"\n";
+}
+
 void CustomFieldDef::GenUsing(std::ostream& s) const {
   s << "using ::bluetooth::";
   for (const auto& c : include_) {
diff --git a/gd/packet/parser/custom_field_def.h b/gd/packet/parser/custom_field_def.h
index 26b954f..f28b116 100644
--- a/gd/packet/parser/custom_field_def.h
+++ b/gd/packet/parser/custom_field_def.h
@@ -35,6 +35,8 @@
 
   void GenInclude(std::ostream& s) const;
 
+  void GenPyBind11Include(std::ostream& s) const;
+
   void GenUsing(std::ostream& s) const;
 
   void GenCustomFieldCheck(std::ostream& s, bool little_endian) const;
diff --git a/gd/packet/parser/custom_type_checker.h b/gd/packet/parser/custom_type_checker.h
index 5d99622..31b4660 100644
--- a/gd/packet/parser/custom_type_checker.h
+++ b/gd/packet/parser/custom_type_checker.h
@@ -36,14 +36,17 @@
   template <class C, bool little_endian, std::optional<Iterator<little_endian>> (*)(C* vec, Iterator<little_endian> it)>
   struct ParseChecker {};
 
+  template <class C, std::string (C::*)() const>
+  struct ToStringChecker {};
+
   template <class C, bool little_endian>
   static int Test(SerializeChecker<C, &C::Serialize>*, SizeChecker<C, &C::size>*,
-                  ParseChecker<C, little_endian, &C::Parse>*);
+                  ParseChecker<C, little_endian, &C::Parse>*, ToStringChecker<C, &C::ToString>*);
 
   template <class C, bool little_endian>
   static char Test(...);
 
-  static constexpr bool value = (sizeof(Test<T, packet_little_endian>(0, 0, 0)) == sizeof(int));
+  static constexpr bool value = (sizeof(Test<T, packet_little_endian>(0, 0, 0, 0)) == sizeof(int));
 };
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/parser/fields/array_field.cc b/gd/packet/parser/fields/array_field.cc
index 37dea16..77687e6 100644
--- a/gd/packet/parser/fields/array_field.cc
+++ b/gd/packet/parser/fields/array_field.cc
@@ -119,7 +119,7 @@
   s << "auto to_bound = begin();";
 
   int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
-  s << GetDataType() << " " << GetName() << "_value;";
+  s << GetDataType() << " " << GetName() << "_value{};";
   s << GetDataType() << "* " << GetName() << "_ptr = &" << GetName() << "_value;";
   GenExtractor(s, num_leading_bits, false);
 
@@ -175,3 +175,23 @@
 const PacketField* ArrayField::GetElementField() const {
   return element_field_;
 }
+
+void ArrayField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  s << "\"ARRAY[\";";
+  s << "/* " << element_field_->GetDataType() << "   " << element_field_->GetFieldType() << " */";
+
+  std::string arr_idx = "arridx_" + accessor;
+  std::string arr_size = std::to_string(array_size_);
+  s << "for (size_t index = 0; index < " << arr_size << "; index++) {";
+  std::string element_accessor = "(" + accessor + "[index])";
+  s << "ss << ((index == 0) ? \"\" : \", \") << ";
+
+  if (element_field_->GetFieldType() == CustomField::kFieldType) {
+    s << element_accessor << ".ToString()";
+  } else {
+    element_field_->GenStringRepresentation(s, element_accessor);
+  }
+
+  s << ";}";
+  s << "ss << \"]\"";
+}
diff --git a/gd/packet/parser/fields/array_field.h b/gd/packet/parser/fields/array_field.h
index dd49f26..e31e8de 100644
--- a/gd/packet/parser/fields/array_field.h
+++ b/gd/packet/parser/fields/array_field.h
@@ -62,6 +62,8 @@
 
   virtual const PacketField* GetElementField() const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
   const std::string name_;
 
   const PacketField* element_field_{nullptr};
diff --git a/gd/packet/parser/fields/body_field.cc b/gd/packet/parser/fields/body_field.cc
index 5e00b68..cab1af5 100644
--- a/gd/packet/parser/fields/body_field.cc
+++ b/gd/packet/parser/fields/body_field.cc
@@ -20,12 +20,23 @@
 
 BodyField::BodyField(ParseLocation loc) : PacketField("body", loc) {}
 
+void BodyField::SetSizeField(const SizeField* size_field) {
+  if (size_field_ != nullptr) {
+    ERROR(this, size_field_, size_field) << "The size field for the body has already been assigned.";
+  }
+  size_field_ = size_field;
+}
+
 const std::string& BodyField::GetFieldType() const {
   return BodyField::kFieldType;
 }
 
 Size BodyField::GetSize() const {
-  return Size(0);
+  if (size_field_ == nullptr) {
+    return Size(0);
+  }
+  std::string dynamic_size = "(" + size_field_->GetName() + " * 8)";
+  return dynamic_size;
 }
 
 std::string BodyField::GetDataType() const {
@@ -60,3 +71,7 @@
 void BodyField::GenValidator(std::ostream&) const {
   // Do nothing
 }
+
+void BodyField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  s << "\"BODY REPRESENTATION_UNIMPLEMENTED " << accessor << " \"";
+}
diff --git a/gd/packet/parser/fields/body_field.h b/gd/packet/parser/fields/body_field.h
index ce4ede7..d8c4ee0 100644
--- a/gd/packet/parser/fields/body_field.h
+++ b/gd/packet/parser/fields/body_field.h
@@ -28,6 +28,8 @@
 
   virtual const std::string& GetFieldType() const override;
 
+  void SetSizeField(const SizeField* size_field);
+
   virtual Size GetSize() const override;
 
   virtual std::string GetDataType() const override;
@@ -47,4 +49,9 @@
   virtual void GenInserter(std::ostream&) const override;
 
   virtual void GenValidator(std::ostream&) const override;
+
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
+  // Body fields can only be dynamically sized.
+  const SizeField* size_field_{nullptr};
 };
diff --git a/gd/packet/parser/fields/checksum_field.cc b/gd/packet/parser/fields/checksum_field.cc
index 4647992..67863c2 100644
--- a/gd/packet/parser/fields/checksum_field.cc
+++ b/gd/packet/parser/fields/checksum_field.cc
@@ -58,3 +58,8 @@
 void ChecksumField::GenValidator(std::ostream&) const {
   // Done in packet_def.cc
 }
+
+void ChecksumField::GenStringRepresentation(std::ostream& s, std::string) const {
+  // TODO: there is currently no way to get checksum value
+  s << "\"CHECKSUM\"";
+}
diff --git a/gd/packet/parser/fields/checksum_field.h b/gd/packet/parser/fields/checksum_field.h
index c15f023..0e0a4c2 100644
--- a/gd/packet/parser/fields/checksum_field.h
+++ b/gd/packet/parser/fields/checksum_field.h
@@ -46,6 +46,8 @@
 
   virtual void GenValidator(std::ostream&) const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
  private:
   std::string type_name_;
 };
diff --git a/gd/packet/parser/fields/checksum_start_field.cc b/gd/packet/parser/fields/checksum_start_field.cc
index 5531c5a..3f87db2 100644
--- a/gd/packet/parser/fields/checksum_start_field.cc
+++ b/gd/packet/parser/fields/checksum_start_field.cc
@@ -61,3 +61,7 @@
 std::string ChecksumStartField::GetStartedFieldName() const {
   return started_field_name_;
 }
+
+void ChecksumStartField::GenStringRepresentation(std::ostream&, std::string) const {
+  // Print nothing for checksum start
+}
diff --git a/gd/packet/parser/fields/checksum_start_field.h b/gd/packet/parser/fields/checksum_start_field.h
index c63806b..8c61a82 100644
--- a/gd/packet/parser/fields/checksum_start_field.h
+++ b/gd/packet/parser/fields/checksum_start_field.h
@@ -51,6 +51,8 @@
 
   virtual std::string GetStartedFieldName() const;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
  private:
   std::string started_field_name_;
 };
diff --git a/gd/packet/parser/fields/custom_field.cc b/gd/packet/parser/fields/custom_field.cc
index 4e387f8..6c5900d 100644
--- a/gd/packet/parser/fields/custom_field.cc
+++ b/gd/packet/parser/fields/custom_field.cc
@@ -91,3 +91,11 @@
 void CustomField::GenValidator(std::ostream&) const {
   // Do nothing.
 }
+
+void CustomField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  s << accessor << "->ToString()";
+}
+
+void CustomField::GenBuilderParameterFromView(std::ostream& s) const {
+  s << "*view.Get" << util::UnderscoreToCamelCase(GetName()) << "()";
+}
diff --git a/gd/packet/parser/fields/custom_field.h b/gd/packet/parser/fields/custom_field.h
index 621a3c8..8e4ae8a 100644
--- a/gd/packet/parser/fields/custom_field.h
+++ b/gd/packet/parser/fields/custom_field.h
@@ -49,6 +49,10 @@
 
   virtual void GenValidator(std::ostream&) const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
+  virtual void GenBuilderParameterFromView(std::ostream& s) const override;
+
  private:
   std::string type_name_;
 };
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.cc b/gd/packet/parser/fields/custom_field_fixed_size.cc
index 687d48d..54463cd 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.cc
+++ b/gd/packet/parser/fields/custom_field_fixed_size.cc
@@ -62,3 +62,8 @@
 void CustomFieldFixedSize::GenValidator(std::ostream&) const {
   // Do nothing.
 }
+
+void CustomFieldFixedSize::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  // We assume that custom fields will have a ToString() method
+  s << accessor << ".ToString()";
+}
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.h b/gd/packet/parser/fields/custom_field_fixed_size.h
index 97acff9..c53f0b0 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.h
+++ b/gd/packet/parser/fields/custom_field_fixed_size.h
@@ -41,5 +41,7 @@
 
   virtual void GenValidator(std::ostream&) const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
   std::string type_name_;
 };
diff --git a/gd/packet/parser/fields/enum_field.cc b/gd/packet/parser/fields/enum_field.cc
index 3080d43..8fe56c2 100644
--- a/gd/packet/parser/fields/enum_field.cc
+++ b/gd/packet/parser/fields/enum_field.cc
@@ -51,3 +51,7 @@
 void EnumField::GenValidator(std::ostream&) const {
   // Do nothing
 }
+
+void EnumField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  s << GetDataType() << "Text(" << accessor << ")";
+}
diff --git a/gd/packet/parser/fields/enum_field.h b/gd/packet/parser/fields/enum_field.h
index 11c64e0..f413553 100644
--- a/gd/packet/parser/fields/enum_field.h
+++ b/gd/packet/parser/fields/enum_field.h
@@ -41,6 +41,8 @@
 
   virtual void GenValidator(std::ostream&) const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
  private:
   EnumDef enum_def_;
   std::string value_;
diff --git a/gd/packet/parser/fields/fixed_scalar_field.cc b/gd/packet/parser/fields/fixed_scalar_field.cc
index 9bfdf0e..e0ad2dd 100644
--- a/gd/packet/parser/fields/fixed_scalar_field.cc
+++ b/gd/packet/parser/fields/fixed_scalar_field.cc
@@ -33,3 +33,7 @@
 void FixedScalarField::GenValue(std::ostream& s) const {
   s << value_;
 }
+
+void FixedScalarField::GenStringRepresentation(std::ostream& s, std::string) const {
+  s << "+" << value_;
+}
diff --git a/gd/packet/parser/fields/fixed_scalar_field.h b/gd/packet/parser/fields/fixed_scalar_field.h
index 0070f6c..0112b27 100644
--- a/gd/packet/parser/fields/fixed_scalar_field.h
+++ b/gd/packet/parser/fields/fixed_scalar_field.h
@@ -33,6 +33,8 @@
 
   virtual std::string GetDataType() const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
   static const std::string field_type;
 
  private:
diff --git a/gd/packet/parser/fields/packet_field.cc b/gd/packet/parser/fields/packet_field.cc
index 7b0bdb7..b51999f 100644
--- a/gd/packet/parser/fields/packet_field.cc
+++ b/gd/packet/parser/fields/packet_field.cc
@@ -100,3 +100,7 @@
 const PacketField* PacketField::GetElementField() const {
   return nullptr;
 }
+
+void PacketField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  s << "\"REPRESENTATION_UNIMPLEMENTED " << GetFieldType() << " " << accessor << "\"";
+}
diff --git a/gd/packet/parser/fields/packet_field.h b/gd/packet/parser/fields/packet_field.h
index d369a92..55653bd 100644
--- a/gd/packet/parser/fields/packet_field.h
+++ b/gd/packet/parser/fields/packet_field.h
@@ -44,8 +44,7 @@
   // For most field types, this will be the same as GetSize();
   virtual Size GetStructSize() const;
 
-  // Get the type of the field to be used in the builders constructor and
-  // variables.
+  // Get the type of the field to be used in the member variables.
   virtual std::string GetDataType() const = 0;
 
   // Given an iterator {name}_it, extract the type.
@@ -107,6 +106,9 @@
   // Get field of nested elements if this is a container field, nullptr if none
   virtual const PacketField* GetElementField() const;
 
+  // Return string representation of this field, that can be displayed for debugging or logging purposes
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const;
+
   std::string GetDebugName() const override;
 
   ParseLocation GetLocation() const override;
diff --git a/gd/packet/parser/fields/payload_field.cc b/gd/packet/parser/fields/payload_field.cc
index c075996..57a1822 100644
--- a/gd/packet/parser/fields/payload_field.cc
+++ b/gd/packet/parser/fields/payload_field.cc
@@ -107,3 +107,8 @@
 void PayloadField::GenValidator(std::ostream&) const {
   // Do nothing
 }
+
+void PayloadField::GenStringRepresentation(std::ostream& s, std::string) const {
+  // TODO: we should parse the child packets
+  s << "\"PAYLOAD[]\"";
+}
diff --git a/gd/packet/parser/fields/payload_field.h b/gd/packet/parser/fields/payload_field.h
index 11e6267..b4ead5b 100644
--- a/gd/packet/parser/fields/payload_field.h
+++ b/gd/packet/parser/fields/payload_field.h
@@ -54,6 +54,8 @@
 
   virtual void GenValidator(std::ostream&) const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
   // Payload fields can only be dynamically sized.
   const SizeField* size_field_;
   // Only used if the size of the payload is based on another field.
diff --git a/gd/packet/parser/fields/scalar_field.cc b/gd/packet/parser/fields/scalar_field.cc
index 320534a..0033681 100644
--- a/gd/packet/parser/fields/scalar_field.cc
+++ b/gd/packet/parser/fields/scalar_field.cc
@@ -99,7 +99,7 @@
   s << "ASSERT(was_validated_);";
   s << "auto to_bound = begin();";
   int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
-  s << GetDataType() << " " << GetName() << "_value;";
+  s << GetDataType() << " " << GetName() << "_value{};";
   s << GetDataType() << "* " << GetName() << "_ptr = &" << GetName() << "_value;";
   GenExtractor(s, num_leading_bits, false);
   s << "return " << GetName() << "_value;";
@@ -129,3 +129,7 @@
 void ScalarField::GenValidator(std::ostream&) const {
   // Do nothing
 }
+
+void ScalarField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  s << "+" << accessor;
+}
diff --git a/gd/packet/parser/fields/scalar_field.h b/gd/packet/parser/fields/scalar_field.h
index 65f897e..e15d6c1 100644
--- a/gd/packet/parser/fields/scalar_field.h
+++ b/gd/packet/parser/fields/scalar_field.h
@@ -49,6 +49,8 @@
 
   virtual void GenValidator(std::ostream&) const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
  private:
   const int size_;
 };
diff --git a/gd/packet/parser/fields/size_field.cc b/gd/packet/parser/fields/size_field.cc
index 0a1b80b..ce2c899 100644
--- a/gd/packet/parser/fields/size_field.cc
+++ b/gd/packet/parser/fields/size_field.cc
@@ -61,3 +61,7 @@
 std::string SizeField::GetSizedFieldName() const {
   return sized_field_name_;
 }
+
+void SizeField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  s << accessor;
+}
diff --git a/gd/packet/parser/fields/size_field.h b/gd/packet/parser/fields/size_field.h
index b6e8639..2d40c42 100644
--- a/gd/packet/parser/fields/size_field.h
+++ b/gd/packet/parser/fields/size_field.h
@@ -46,6 +46,8 @@
 
   virtual std::string GetSizedFieldName() const;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
  private:
   int size_;
   std::string sized_field_name_;
diff --git a/gd/packet/parser/fields/struct_field.cc b/gd/packet/parser/fields/struct_field.cc
index fbdafcf..8013a78 100644
--- a/gd/packet/parser/fields/struct_field.cc
+++ b/gd/packet/parser/fields/struct_field.cc
@@ -56,7 +56,7 @@
   s << "size_t end_index = size();";
   s << "auto to_bound = begin();";
   int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
-  s << GetDataType() << " " << GetName() << "_value;";
+  s << GetDataType() << " " << GetName() << "_value{};";
   s << GetDataType() << "* " << GetName() << "_ptr = &" << GetName() << "_value;";
   GenExtractor(s, num_leading_bits, false);
 
@@ -83,3 +83,7 @@
 void StructField::GenValidator(std::ostream&) const {
   // Do nothing
 }
+
+void StructField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  s << accessor << ".ToString()";
+}
diff --git a/gd/packet/parser/fields/struct_field.h b/gd/packet/parser/fields/struct_field.h
index 1f4f100..9d1d463 100644
--- a/gd/packet/parser/fields/struct_field.h
+++ b/gd/packet/parser/fields/struct_field.h
@@ -49,6 +49,8 @@
 
   virtual void GenValidator(std::ostream&) const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
  private:
   std::string type_name_;
 
diff --git a/gd/packet/parser/fields/variable_length_struct_field.cc b/gd/packet/parser/fields/variable_length_struct_field.cc
index 51a46e4..35f2f54 100644
--- a/gd/packet/parser/fields/variable_length_struct_field.cc
+++ b/gd/packet/parser/fields/variable_length_struct_field.cc
@@ -61,7 +61,7 @@
   s << "size_t end_index = size();";
   s << "auto to_bound = begin();";
   int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
-  s << "std::unique_ptr<" << type_name_ << "> " << GetName() << "_ptr;";
+  s << GetDataType() << " " << GetName() << "_ptr{};";
   GenExtractor(s, num_leading_bits, false);
   s << "return " << GetName() << "_ptr;";
   s << "}\n";
diff --git a/gd/packet/parser/fields/vector_field.cc b/gd/packet/parser/fields/vector_field.cc
index ab47c4f..cc5cead 100644
--- a/gd/packet/parser/fields/vector_field.cc
+++ b/gd/packet/parser/fields/vector_field.cc
@@ -89,15 +89,15 @@
 
   // size_field_ is of type SIZE
   if (size_field_->GetFieldType() == SizeField::kFieldType) {
-    std::string ret = "(static_cast<size_t>(" + size_field_->GetName() + "_extracted) * 8)";
+    std::string ret = "(static_cast<size_t>(to_fill->" + size_field_->GetName() + "_extracted_) * 8)";
     if (!size_modifier_.empty()) ret += "-" + size_modifier_;
     return ret;
   }
 
   // size_field_ is of type COUNT and elements have a fixed size
   if (!element_size_.empty() && !element_size_.has_dynamic()) {
-    return "(static_cast<size_t>(" + size_field_->GetName() + "_extracted) * " + std::to_string(element_size_.bits()) +
-           ")";
+    return "(static_cast<size_t>(to_fill->" + size_field_->GetName() + "_extracted_) * " +
+           std::to_string(element_size_.bits()) + ")";
   }
 
   return Size();
@@ -112,7 +112,7 @@
   if (size_field_ != nullptr && size_field_->GetFieldType() == CountField::kFieldType) {
     s << "size_t " << element_field_->GetName() << "_count = ";
     if (for_struct) {
-      s << size_field_->GetName() << "_extracted;";
+      s << "to_fill->" << size_field_->GetName() << "_extracted_;";
     } else {
       s << "Get" << util::UnderscoreToCamelCase(size_field_->GetName()) << "();";
     }
@@ -157,7 +157,7 @@
   s << "auto to_bound = begin();";
 
   int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
-  s << GetDataType() << " " << GetName() << "_value;";
+  s << GetDataType() << " " << GetName() << "_value{};";
   s << GetDataType() << "* " << GetName() << "_ptr = &" << GetName() << "_value;";
   GenExtractor(s, num_leading_bits, false);
 
@@ -230,3 +230,22 @@
 const PacketField* VectorField::GetElementField() const {
   return element_field_;
 }
+
+void VectorField::GenStringRepresentation(std::ostream& s, std::string accessor) const {
+  s << "\"VECTOR[\";";
+
+  std::string arr_idx = "arridx_" + accessor;
+  std::string vec_size = accessor + ".size()";
+  s << "for (size_t index = 0; index < " << vec_size << "; index++) {";
+  std::string element_accessor = "(" + accessor + "[index])";
+  s << "ss << ((index == 0) ? \"\" : \", \") << ";
+
+  if (element_field_->GetFieldType() == CustomField::kFieldType) {
+    s << element_accessor << ".ToString()";
+  } else {
+    element_field_->GenStringRepresentation(s, element_accessor);
+  }
+
+  s << ";}";
+  s << "ss << \"]\"";
+}
diff --git a/gd/packet/parser/fields/vector_field.h b/gd/packet/parser/fields/vector_field.h
index b2ae95d..827f718 100644
--- a/gd/packet/parser/fields/vector_field.h
+++ b/gd/packet/parser/fields/vector_field.h
@@ -67,6 +67,8 @@
 
   virtual const PacketField* GetElementField() const override;
 
+  virtual void GenStringRepresentation(std::ostream& s, std::string accessor) const override;
+
   const std::string name_;
 
   const PacketField* element_field_{nullptr};
diff --git a/gd/packet/parser/language_y.yy b/gd/packet/parser/language_y.yy
index a36abef..85a867a 100644
--- a/gd/packet/parser/language_y.yy
+++ b/gd/packet/parser/language_y.yy
@@ -22,14 +22,14 @@
 %parse-param { Declarations* decls }
 %lex-param { void* scanner }
 
-%pure-parser
 %glr-parser
 %skeleton "glr.cc"
 
 %expect-rr 0
 
 %debug
-%error-verbose
+%define parse.error verbose
+%locations
 %verbose
 
 %union {
@@ -618,6 +618,11 @@
       DEBUG() << "Size for payload defined\n";
       $$ = new SizeField("payload", $6, LOC);
     }
+  | SIZE '(' BODY ')' ':' INTEGER
+    {
+      DEBUG() << "Size for body defined\n";
+      $$ = new SizeField("body", $6, LOC);
+    }
   | COUNT '(' IDENTIFIER ')' ':' INTEGER
     {
       DEBUG() << "Count field defined\n";
diff --git a/gd/packet/parser/main.cc b/gd/packet/parser/main.cc
index 757523d..d765ef3 100644
--- a/gd/packet/parser/main.cc
+++ b/gd/packet/parser/main.cc
@@ -121,6 +121,7 @@
   out_file << "#pragma once\n";
   out_file << "\n\n";
   out_file << "#include <stdint.h>\n";
+  out_file << "#include <sstream>\n";
   out_file << "#include <string>\n";
   out_file << "#include <functional>\n";
   out_file << "\n\n";
@@ -131,6 +132,9 @@
   out_file << "#include \"packet/packet_builder.h\"\n";
   out_file << "#include \"packet/packet_struct.h\"\n";
   out_file << "#include \"packet/packet_view.h\"\n";
+  out_file << "\n#if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)\n";
+  out_file << "#include \"packet/raw_builder.h\"\n";
+  out_file << "\n#endif\n";
   out_file << "#include \"packet/parser/checksum_type_checker.h\"\n";
   out_file << "#include \"packet/parser/custom_type_checker.h\"\n";
   out_file << "\n\n";
@@ -164,6 +168,9 @@
   out_file << "using ::bluetooth::packet::PacketBuilder;";
   out_file << "using ::bluetooth::packet::PacketStruct;";
   out_file << "using ::bluetooth::packet::PacketView;";
+  out_file << "\n#if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)\n";
+  out_file << "using ::bluetooth::packet::RawBuilder;";
+  out_file << "\n#endif\n";
   out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
   out_file << "\n\n";
 
@@ -266,6 +273,17 @@
     out_file << "\n\n";
     out_file << "#include " << gen_relative_header << "\n";
     out_file << "\n\n";
+    out_file << "#include \"packet/raw_builder.h\"\n";
+    out_file << "\n\n";
+
+    for (const auto& c : decls.type_defs_queue_) {
+      if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) {
+        const auto* custom_def = dynamic_cast<const CustomFieldDef*>(c.second);
+        custom_def->GenPyBind11Include(out_file);
+      }
+    }
+
+    out_file << "\n\n";
 
     generate_namespace_open(namespace_list, out_file);
     out_file << "\n\n";
@@ -288,6 +306,7 @@
     out_file << "using ::bluetooth::packet::BaseStruct;";
     out_file << "using ::bluetooth::packet::PacketStruct;";
     out_file << "using ::bluetooth::packet::PacketView;";
+    out_file << "using ::bluetooth::packet::RawBuilder;";
     out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
     out_file << "\n\n";
 
@@ -365,9 +384,7 @@
                   << "(py::module& m);\n";
   }
 
-  out_file_main << "void define_" << input_filename << "_submodule(py::module& parent) {\n\n";
-  out_file_main << "py::module m = parent.def_submodule(\"" << input_filename << "\", \"A submodule of "
-                << input_filename << "\");\n\n";
+  out_file_main << "void define_" << input_filename << "_submodule(py::module& m) {\n\n";
   for (size_t i = 0; i < out_file_shards.size(); i++) {
     out_file_main << "define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(m);\n";
   }
diff --git a/gd/packet/parser/packet_def.cc b/gd/packet/parser/packet_def.cc
index 2fd4df2..9305207 100644
--- a/gd/packet/parser/packet_def.cc
+++ b/gd/packet/parser/packet_def.cc
@@ -47,6 +47,8 @@
     s << "{ return " << name_ << "View(packet); }";
   }
 
+  GenTestingParserFromBytes(s);
+
   std::set<std::string> fixed_types = {
       FixedScalarField::kFieldType,
       FixedEnumField::kFieldType,
@@ -62,6 +64,10 @@
   GenValidator(s);
   s << "\n";
 
+  s << " public:";
+  GenParserToString(s);
+  s << "\n";
+
   s << " protected:\n";
   // Constructor from a View
   if (parent_ != nullptr) {
@@ -84,6 +90,29 @@
   s << "};\n";
 }
 
+void PacketDef::GenTestingParserFromBytes(std::ostream& s) const {
+  s << "\n#if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)\n";
+
+  s << "static " << name_ << "View FromBytes(std::vector<uint8_t> bytes) {";
+  s << "auto vec = std::make_shared<std::vector<uint8_t>>(bytes);";
+  s << "return " << name_ << "View::Create(";
+  auto ancestor_ptr = parent_;
+  size_t parent_parens = 0;
+  while (ancestor_ptr != nullptr) {
+    s << ancestor_ptr->name_ << "View::Create(";
+    parent_parens++;
+    ancestor_ptr = ancestor_ptr->parent_;
+  }
+  s << "vec";
+  for (size_t i = 0; i < parent_parens; i++) {
+    s << ")";
+  }
+  s << ");";
+  s << "}";
+
+  s << "\n#endif\n";
+}
+
 void PacketDef::GenParserDefinitionPybind11(std::ostream& s) const {
   s << "py::class_<" << name_ << "View";
   if (parent_ != nullptr) {
@@ -283,6 +312,35 @@
   }
 }
 
+void PacketDef::GenParserToString(std::ostream& s) const {
+  s << "virtual std::string ToString() " << (parent_ != nullptr ? " override" : "") << " {";
+  s << "std::stringstream ss;";
+  s << "ss << std::showbase << std::hex << \"" << name_ << " { \";";
+
+  if (fields_.size() > 0) {
+    s << "ss << \"\" ";
+    bool firstfield = true;
+    for (const auto& field : fields_) {
+      if (field->GetFieldType() == ReservedField::kFieldType || field->GetFieldType() == FixedScalarField::kFieldType ||
+          field->GetFieldType() == ChecksumStartField::kFieldType)
+        continue;
+
+      s << (firstfield ? " << \"" : " << \", ") << field->GetName() << " = \" << ";
+
+      field->GenStringRepresentation(s, field->GetGetterFunctionName() + "()");
+
+      if (firstfield) {
+        firstfield = false;
+      }
+    }
+    s << ";";
+  }
+
+  s << "ss << \" }\";";
+  s << "return ss.str();";
+  s << "}\n";
+}
+
 void PacketDef::GenBuilderDefinition(std::ostream& s) const {
   s << "class " << name_ << "Builder";
   if (parent_ != nullptr) {
@@ -301,6 +359,9 @@
   if (!fields_.HasBody()) {
     GenBuilderCreate(s);
     s << "\n";
+
+    GenTestingFromView(s);
+    s << "\n";
   }
 
   GenSerialize(s);
@@ -326,6 +387,26 @@
   s << "\n";
 }
 
+void PacketDef::GenTestingFromView(std::ostream& s) const {
+  s << "#if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)\n";
+
+  s << "static std::unique_ptr<" << name_ << "Builder> FromView(" << name_ << "View view) {";
+  s << "return " << name_ << "Builder::Create(";
+  FieldList params = GetParamList().GetFieldsWithoutTypes({
+      BodyField::kFieldType,
+  });
+  for (int i = 0; i < params.size(); i++) {
+    params[i]->GenBuilderParameterFromView(s);
+    if (i != params.size() - 1) {
+      s << ", ";
+    }
+  }
+  s << ");";
+  s << "}";
+
+  s << "\n#endif\n";
+}
+
 void PacketDef::GenBuilderDefinitionPybind11(std::ostream& s) const {
   s << "py::class_<" << name_ << "Builder";
   if (parent_ != nullptr) {
@@ -337,6 +418,7 @@
       s << ", PacketBuilder<!kLittleEndian>";
     }
   }
+  s << ", std::shared_ptr<" << name_ << "Builder>";
   s << ">(m, \"" << name_ << "Builder\")";
   if (!fields_.HasBody()) {
     GenBuilderCreatePybind11(s);
@@ -355,39 +437,16 @@
   s << "class " << name_ << "ReflectionTest : public testing::TestWithParam<std::vector<uint8_t>> { ";
   s << "public: ";
   s << "void CompareBytes(std::vector<uint8_t> captured_packet) {";
-  s << "auto vec = std::make_shared<std::vector<uint8_t>>(captured_packet.begin(), captured_packet.end());";
-  s << name_ << "View view = " << name_ << "View::Create(";
-  auto ancestor_ptr = parent_;
-  size_t parent_parens = 0;
-  while (ancestor_ptr != nullptr) {
-    s << ancestor_ptr->name_ << "View::Create(";
-    parent_parens++;
-    ancestor_ptr = ancestor_ptr->parent_;
-  }
-  s << "vec";
-  for (size_t i = 0; i < parent_parens; i++) {
-    s << ")";
-  }
-  s << ");";
+  s << name_ << "View view = " << name_ << "View::FromBytes(captured_packet);";
   s << "if (!view.IsValid()) { LOG_INFO(\"Invalid Packet Bytes (size = %zu)\", view.size());";
   s << "for (size_t i = 0; i < view.size(); i++) { LOG_DEBUG(\"%5zd:%02X\", i, *(view.begin() + i)); }}";
   s << "ASSERT_TRUE(view.IsValid());";
-  s << "auto packet = " << name_ << "Builder::Create(";
-  FieldList params = GetParamList().GetFieldsWithoutTypes({
-      BodyField::kFieldType,
-  });
-  for (int i = 0; i < params.size(); i++) {
-    params[i]->GenBuilderParameterFromView(s);
-    if (i != params.size() - 1) {
-      s << ", ";
-    }
-  }
-  s << ");";
+  s << "auto packet = " << name_ << "Builder::FromView(view);";
   s << "std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();";
   s << "packet_bytes->reserve(packet->size());";
   s << "BitInserter it(*packet_bytes);";
   s << "packet->Serialize(it);";
-  s << "ASSERT_EQ(*packet_bytes, *vec);";
+  s << "ASSERT_EQ(*packet_bytes, captured_packet);";
   s << "}";
   s << "};";
   s << "TEST_P(" << name_ << "ReflectionTest, generatedReflectionTest) {";
@@ -399,40 +458,22 @@
 }
 
 void PacketDef::GenFuzzTestDefine(std::ostream& s) const {
-  s << "#ifdef PACKET_FUZZ_TESTING\n";
-  s << "#define DEFINE_AND_REGISTER_" << name_ << "ReflectionFuzzTest(REGISTRY) ";
+  s << "#if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING)\n";
+  s << "#define DEFINE_" << name_ << "ReflectionFuzzTest() ";
   s << "void Run" << name_ << "ReflectionFuzzTest(const uint8_t* data, size_t size) {";
-  s << "auto vec = std::make_shared<std::vector<uint8_t>>(data, data + size);";
-  s << name_ << "View view = " << name_ << "View::Create(";
-  auto ancestor_ptr = parent_;
-  size_t parent_parens = 0;
-  while (ancestor_ptr != nullptr) {
-    s << ancestor_ptr->name_ << "View::Create(";
-    parent_parens++;
-    ancestor_ptr = ancestor_ptr->parent_;
-  }
-  s << "vec";
-  for (size_t i = 0; i < parent_parens; i++) {
-    s << ")";
-  }
-  s << ");";
+  s << "auto vec = std::vector<uint8_t>(data, data + size);";
+  s << name_ << "View view = " << name_ << "View::FromBytes(vec);";
   s << "if (!view.IsValid()) { return; }";
-  s << "auto packet = " << name_ << "Builder::Create(";
-  FieldList params = GetParamList().GetFieldsWithoutTypes({
-      BodyField::kFieldType,
-  });
-  for (int i = 0; i < params.size(); i++) {
-    params[i]->GenBuilderParameterFromView(s);
-    if (i != params.size() - 1) {
-      s << ", ";
-    }
-  }
-  s << ");";
+  s << "auto packet = " << name_ << "Builder::FromView(view);";
   s << "std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();";
   s << "packet_bytes->reserve(packet->size());";
   s << "BitInserter it(*packet_bytes);";
   s << "packet->Serialize(it);";
   s << "}";
+  s << "\n#endif\n";
+  s << "#ifdef PACKET_FUZZ_TESTING\n";
+  s << "#define DEFINE_AND_REGISTER_" << name_ << "ReflectionFuzzTest(REGISTRY) ";
+  s << "DEFINE_" << name_ << "ReflectionFuzzTest();";
   s << " class " << name_ << "ReflectionFuzzTestRegistrant {";
   s << "public: ";
   s << "explicit " << name_
@@ -496,7 +537,6 @@
   s << ".def(py::init([](";
   auto params = GetParamList();
   std::vector<std::string> constructor_args;
-  std::vector<std::string> keep_alive_args;
   int i = 1;
   for (const auto& param : params) {
     i++;
@@ -508,7 +548,6 @@
     // Use shared_ptr instead of unique_ptr for the Python interface
     if (param->BuilderParameterMustBeMoved()) {
       param_type = util::StringFindAndReplaceAll(param_type, "unique_ptr", "shared_ptr");
-      keep_alive_args.push_back(std::to_string(i));
     }
     ss << param_type << " " << param->GetName();
     constructor_args.push_back(ss.str());
@@ -528,19 +567,37 @@
     auto move_only_param_name = param->GetName() + "_move_only";
     s << param_type << " " << move_only_param_name << ";";
     if (param->IsContainerField()) {
-      // Assume single layer container
+      // Assume single layer container and copy it
+      auto struct_type = param->GetElementField()->GetDataType();
+      struct_type = util::StringFindAndReplaceAll(struct_type, "std::unique_ptr<", "");
+      struct_type = util::StringFindAndReplaceAll(struct_type, ">", "");
       s << "for (size_t i = 0; i < " << param->GetName() << ".size(); i++) {";
+      // Serialize each struct
+      s << "auto " << param->GetName() + "_bytes = std::make_shared<std::vector<uint8_t>>();";
+      s << param->GetName() + "_bytes->reserve(" << param->GetName() << "[i]->size());";
+      s << "BitInserter " << param->GetName() + "_bi(*" << param->GetName() << "_bytes);";
+      s << param->GetName() << "[i]->Serialize(" << param->GetName() << "_bi);";
+      // Parse it again
+      s << "auto " << param->GetName() << "_view = PacketView<kLittleEndian>(" << param->GetName() << "_bytes);";
+      s << param->GetElementField()->GetDataType() << " " << param->GetName() << "_reparsed = ";
+      s << "Parse" << struct_type << "(" << param->GetName() + "_view.begin());";
+      // Push it into a new container
       if (param->GetFieldType() == VectorField::kFieldType) {
-        s << move_only_param_name << ".emplace_back(" << param->GetName() << "[i].get());";
+        s << move_only_param_name << ".push_back(std::move(" << param->GetName() + "_reparsed));";
       } else if (param->GetFieldType() == ArrayField::kFieldType) {
-        s << move_only_param_name << "[i].reset(" << param->GetName() << "[i].get());";
+        s << move_only_param_name << "[i] = std::move(" << param->GetName() << "_reparsed);";
       } else {
         ERROR() << param << " is not supported by Pybind11";
       }
       s << "}";
     } else {
-      // Release shared_ptr to unique_ptr and leave the Python copy as nullptr and to be garbage collected by Python
-      s << move_only_param_name << ".reset(" << param->GetName() << ".get());";
+      // Serialize the parameter and pass the bytes in a RawBuilder
+      s << "std::vector<uint8_t> " << param->GetName() + "_bytes;";
+      s << param->GetName() + "_bytes.reserve(" << param->GetName() << "->size());";
+      s << "BitInserter " << param->GetName() + "_bi(" << param->GetName() << "_bytes);";
+      s << param->GetName() << "->Serialize(" << param->GetName() + "_bi);";
+      s << move_only_param_name << " = ";
+      s << "std::make_unique<RawBuilder>(" << param->GetName() << "_bytes);";
     }
   }
   s << "return " << name_ << "Builder::Create(";
@@ -560,11 +617,7 @@
     builder_vars.push_back(ss.str());
   }
   s << util::StringJoin(",", builder_vars) << ");}";
-  if (keep_alive_args.empty()) {
-    s << "))";
-  } else {
-    s << "), py::keep_alive<1," << util::StringJoin(",", keep_alive_args) << ">())";
-  }
+  s << "))";
 }
 
 void PacketDef::GenBuilderParameterChecker(std::ostream& s) const {
diff --git a/gd/packet/parser/packet_def.h b/gd/packet/parser/packet_def.h
index e8acdc3..9354b97 100644
--- a/gd/packet/parser/packet_def.h
+++ b/gd/packet/parser/packet_def.h
@@ -33,12 +33,16 @@
 
   void GenParserDefinition(std::ostream& s) const;
 
+  void GenTestingParserFromBytes(std::ostream& s) const;
+
   void GenParserDefinitionPybind11(std::ostream& s) const;
 
   void GenParserFieldGetter(std::ostream& s, const PacketField* field) const;
 
   void GenValidator(std::ostream& s) const;
 
+  void GenParserToString(std::ostream& s) const;
+
   TypeDef::Type GetDefinitionType() const;
 
   void GenBuilderDefinition(std::ostream& s) const;
@@ -58,4 +62,6 @@
   void GenBuilderParameterChecker(std::ostream& s) const;
 
   void GenBuilderConstructor(std::ostream& s) const;
+
+  void GenTestingFromView(std::ostream& s) const;
 };
diff --git a/gd/packet/parser/parent_def.cc b/gd/packet/parser/parent_def.cc
index 1557bf6..20da6cd 100644
--- a/gd/packet/parser/parent_def.cc
+++ b/gd/packet/parser/parent_def.cc
@@ -90,6 +90,12 @@
       continue;
     }
 
+    if (var_len_field->GetFieldType() == BodyField::kFieldType) {
+      const auto& body_field = static_cast<BodyField*>(var_len_field);
+      body_field->SetSizeField(size_field);
+      continue;
+    }
+
     if (var_len_field->GetFieldType() == VectorField::kFieldType) {
       const auto& vector_field = static_cast<VectorField*>(var_len_field);
       vector_field->SetSizeField(size_field);
@@ -241,7 +247,7 @@
   // Add the parameter list.
   for (int i = 0; i < fields_.size(); i++) {
     if (fields_[i]->GenBuilderMember(s)) {
-      s << "_;";
+      s << "_{};";
     }
   }
 }
@@ -312,6 +318,16 @@
   if (fields_.HasPayload()) {
     s << "+ payload_->size()";
   }
+  if (fields_.HasBody()) {
+    for (const auto& field : header_fields) {
+      if (field->GetFieldType() == SizeField::kFieldType) {
+        const auto& field_name = ((SizeField*)field)->GetSizedFieldName();
+        if (field_name == "body") {
+          s << "+ body_size_extracted_";
+        }
+      }
+    }
+  }
   s << " + (BitsOfFooter() / 8);";
   s << "}\n";
 }
@@ -351,6 +367,13 @@
         }
         s << "ASSERT(payload_bytes < (static_cast<size_t>(1) << " << field->GetSize().bits() << "));";
         s << "insert(static_cast<" << field->GetDataType() << ">(payload_bytes), i," << field->GetSize().bits() << ");";
+      } else if (sized_field->GetFieldType() == BodyField::kFieldType) {
+        s << field->GetName() << "_extracted_ = 0;";
+        s << "size_t local_size = " << name_ << "::size();";
+
+        s << "ASSERT((size() - local_size) < (static_cast<size_t>(1) << " << field->GetSize().bits() << "));";
+        s << "insert(static_cast<" << field->GetDataType() << ">(size() - local_size), i," << field->GetSize().bits()
+          << ");";
       } else {
         if (sized_field->GetFieldType() != VectorField::kFieldType) {
           ERROR(field) << __func__ << ": Unhandled sized field type for " << field_name;
diff --git a/gd/packet/parser/size.h b/gd/packet/parser/size.h
index e73bff0..fed3c51 100644
--- a/gd/packet/parser/size.h
+++ b/gd/packet/parser/size.h
@@ -79,7 +79,8 @@
   }
 
   int bytes() const {
-    return bits_ / 8;
+    // Round up to the nearest byte
+    return (bits_ + 7) / 8;
   }
 
   Size operator+(int rhs) {
diff --git a/gd/packet/parser/struct_def.cc b/gd/packet/parser/struct_def.cc
index 16ef742..b174836 100644
--- a/gd/packet/parser/struct_def.cc
+++ b/gd/packet/parser/struct_def.cc
@@ -45,6 +45,37 @@
   s << "}";
 }
 
+void StructDef::GenToString(std::ostream& s) const {
+  s << "std::string ToString() {";
+  s << "std::stringstream ss;";
+  s << "ss << std::hex << std::showbase << \"" << name_ << " { \";";
+
+  if (fields_.size() > 0) {
+    s << "ss";
+    bool firstfield = true;
+    for (const auto& field : fields_) {
+      if (field->GetFieldType() == ReservedField::kFieldType ||
+          field->GetFieldType() == ChecksumStartField::kFieldType ||
+          field->GetFieldType() == FixedScalarField::kFieldType || field->GetFieldType() == CountField::kFieldType ||
+          field->GetFieldType() == SizeField::kFieldType)
+        continue;
+
+      s << (firstfield ? " << \"" : " << \", ") << field->GetName() << " = \" << ";
+
+      field->GenStringRepresentation(s, field->GetName() + "_");
+
+      if (firstfield) {
+        firstfield = false;
+      }
+    }
+    s << ";";
+  }
+
+  s << "ss << \" }\";";
+  s << "return ss.str();";
+  s << "}\n";
+}
+
 void StructDef::GenParse(std::ostream& s) const {
   std::string iterator = (is_little_endian_ ? "Iterator<kLittleEndian>" : "Iterator<!kLittleEndian>");
 
@@ -81,26 +112,33 @@
 
   if (!fields_.HasBody()) {
     s << "size_t end_index = struct_begin_it.NumBytesRemaining();";
-    if (parent_ != nullptr) {
-      s << "if (end_index < " << GetSize().bytes() << " - to_fill->" << parent_->name_ << "::size())";
-    } else {
-      s << "if (end_index < " << GetSize().bytes() << ")";
-    }
+    s << "if (end_index < " << GetSize().bytes() << ")";
     s << "{ return struct_begin_it.Subrange(0,0);}";
   }
 
+  Size total_bits{0};
+  for (const auto& field : fields_) {
+    if (field->GetFieldType() != ReservedField::kFieldType && field->GetFieldType() != BodyField::kFieldType &&
+        field->GetFieldType() != FixedScalarField::kFieldType &&
+        field->GetFieldType() != ChecksumStartField::kFieldType && field->GetFieldType() != ChecksumField::kFieldType &&
+        field->GetFieldType() != CountField::kFieldType) {
+      total_bits += field->GetSize().bits();
+    }
+  }
+  s << "{";
+  s << "if (to_bound.NumBytesRemaining() < " << total_bits.bytes() << ")";
+  if (!fields_.HasBody()) {
+    s << "{ return to_bound.Subrange(to_bound.NumBytesRemaining(),0);}";
+  } else {
+    s << "{ return {};}";
+  }
+  s << "}";
   for (const auto& field : fields_) {
     if (field->GetFieldType() != ReservedField::kFieldType && field->GetFieldType() != BodyField::kFieldType &&
         field->GetFieldType() != FixedScalarField::kFieldType && field->GetFieldType() != SizeField::kFieldType &&
         field->GetFieldType() != ChecksumStartField::kFieldType && field->GetFieldType() != ChecksumField::kFieldType &&
         field->GetFieldType() != CountField::kFieldType) {
       s << "{";
-      s << "if (to_bound.NumBytesRemaining() < " << field->GetSize().bytes() << ")";
-      if (!fields_.HasBody()) {
-        s << "{ return to_bound.Subrange(to_bound.NumBytesRemaining(),0);}";
-      } else {
-        s << "{ return {};}";
-      }
       int num_leading_bits =
           field->GenBounds(s, GetStructOffsetForField(field->GetName()), Size(), field->GetStructSize());
       s << "auto " << field->GetName() << "_ptr = &to_fill->" << field->GetName() << "_;";
@@ -108,22 +146,15 @@
       s << "}";
     }
     if (field->GetFieldType() == CountField::kFieldType || field->GetFieldType() == SizeField::kFieldType) {
-      s << field->GetDataType() << " " << field->GetName() << "_extracted;";
       s << "{";
-      s << "if (to_bound.NumBytesRemaining() < " << field->GetSize().bytes() << ")";
-      if (!fields_.HasBody()) {
-        s << "{ return to_bound.Subrange(to_bound.NumBytesRemaining(),0);}";
-      } else {
-        s << "{ return {};}";
-      }
       int num_leading_bits =
           field->GenBounds(s, GetStructOffsetForField(field->GetName()), Size(), field->GetStructSize());
-      s << "auto " << field->GetName() << "_ptr = &" << field->GetName() << "_extracted;";
+      s << "auto " << field->GetName() << "_ptr = &to_fill->" << field->GetName() << "_extracted_;";
       field->GenExtractor(s, num_leading_bits, true);
       s << "}";
     }
   }
-  s << "return struct_begin_it + to_fill->" << name_ << "::size();";
+  s << "return struct_begin_it + to_fill->size();";
   s << "}";
 }
 
@@ -171,7 +202,16 @@
   GenSpecialize(s);
   s << "\n";
 
+  GenToString(s);
+  s << "\n";
+
   GenMembers(s);
+  for (const auto& field : fields_) {
+    if (field->GetFieldType() == CountField::kFieldType || field->GetFieldType() == SizeField::kFieldType) {
+      s << "\n private:\n";
+      s << " mutable " << field->GetDataType() << " " << field->GetName() << "_extracted_{0};";
+    }
+  }
   s << "};\n";
 
   if (fields_.HasBody()) {
@@ -191,6 +231,7 @@
       s << ", PacketStruct<!kLittleEndian>";
     }
   }
+  s << ", std::shared_ptr<" << name_ << ">";
   s << ">(m, \"" << name_ << "\")";
   s << ".def(py::init<>())";
   s << ".def(\"Serialize\", [](" << GetTypeName() << "& obj){";
diff --git a/gd/packet/parser/struct_def.h b/gd/packet/parser/struct_def.h
index 74c1b04..b4c890b 100644
--- a/gd/packet/parser/struct_def.h
+++ b/gd/packet/parser/struct_def.h
@@ -36,6 +36,8 @@
 
   void GenSpecialize(std::ostream& s) const;
 
+  void GenToString(std::ostream& s) const;
+
   void GenParse(std::ostream& s) const;
 
   void GenParseFunctionPrototype(std::ostream& s) const;
diff --git a/gd/packet/parser/struct_parser_generator.cc b/gd/packet/parser/struct_parser_generator.cc
index 352219e..d2c7d03 100644
--- a/gd/packet/parser/struct_parser_generator.cc
+++ b/gd/packet/parser/struct_parser_generator.cc
@@ -80,7 +80,7 @@
     s << "std::make_unique<" << node.struct_def_->name_ << ">();";
 
     s << "auto " << field->GetName() << "_it = to_bound;";
-    s << "auto optional_it = ";
+    s << "auto parent_optional_it = ";
     s << node.struct_def_->name_ << "::Parse( " << field->GetName() << "_value.get(), ";
     s << field->GetName() << "_it";
     if (node.struct_def_->parent_ != nullptr) {
@@ -88,8 +88,8 @@
     } else {
       s << ");";
     }
-    s << "if (optional_it) {";
-    s << field->GetName() << "_it = *optional_it;";
+    s << "if (parent_optional_it) {";
+    s << field->GetName() << "_it = *parent_optional_it;";
     s << "} else { return nullptr; }";
 
     explore_children(node, s);
diff --git a/gd/packet/parser/test/generated_packet_test.cc b/gd/packet/parser/test/generated_packet_test.cc
index f8454a2..17e026f 100644
--- a/gd/packet/parser/test/generated_packet_test.cc
+++ b/gd/packet/parser/test/generated_packet_test.cc
@@ -1667,15 +1667,15 @@
   parent_vector[3] = std::move(fbs);
 
   std::array<std::unique_ptr<UnusedParentStruct>, 4> vector_copy;
-  size_t i = 0;
+  size_t index = 0;
   for (auto& s : parent_vector) {
     if (s->struct_type_ == StructType::TWO_BYTE) {
-      vector_copy[i] = std::make_unique<TwoByteStruct>(*(TwoByteStruct*)s.get());
+      vector_copy[index] = std::make_unique<TwoByteStruct>(*(TwoByteStruct*)s.get());
     }
     if (s->struct_type_ == StructType::FOUR_BYTE) {
-      vector_copy[i] = std::make_unique<FourByteStruct>(*(FourByteStruct*)s.get());
+      vector_copy[index] = std::make_unique<FourByteStruct>(*(FourByteStruct*)s.get());
     }
-    i++;
+    index++;
   }
 
   auto packet = OneGenericStructFourArrayBuilder::Create(std::move(parent_vector));
@@ -1877,6 +1877,90 @@
 
 DEFINE_AND_INSTANTIATE_ByteSizedFieldsReflectionTest(byte_sized);
 
+TEST(GeneratedPacketTest, testOneGenericStructArrayNoZeroEmpty) {
+  auto too_few_bytes = std::make_shared<std::vector<uint8_t>>(0);
+  auto view = OneGenericStructArrayNoZeroView::Create(too_few_bytes);
+  for (size_t i = 0; i < 10; i++) {
+    if (view.IsValid()) {
+      view.GetAnArray().size();
+    }
+    too_few_bytes->push_back(0);
+    view = OneGenericStructArrayNoZeroView::Create(too_few_bytes);
+  }
+
+  std::vector<uint8_t> a_two_byte_struct = {
+      static_cast<uint8_t>(StructTypeNoZero::TWO_BYTE),
+      0x01,
+      0x02,
+  };
+  too_few_bytes = std::make_shared<std::vector<uint8_t>>(a_two_byte_struct);
+  view = OneGenericStructArrayNoZeroView::Create(too_few_bytes);
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(1, view.GetAnArray().size());
+}
+
+TEST(GeneratedPacketTest, testToStringOutput) {
+  std::vector<TwoRelatedNumbersBe> count_array;
+  for (uint8_t i = 1; i < 5; i++) {
+    TwoRelatedNumbersBe trn;
+    trn.id_ = i;
+    trn.count_ = 0x0102 * i;
+    count_array.push_back(trn);
+  }
+
+  auto packet = ArrayOfStructBeBuilder::Create(count_array);
+
+  ASSERT_EQ(array_of_struct_be.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(array_of_struct_be.size(), packet_bytes->size());
+  for (size_t i = 0; i < array_of_struct_be.size(); i++) {
+    ASSERT_EQ(array_of_struct_be[i], packet_bytes->at(i));
+  }
+
+  PacketView<!kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = ArrayOfStructBeView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+
+  ASSERT_EQ(
+      "ArrayOfStructBe { array_count = 0x4, array = VECTOR[TwoRelatedNumbersBe { id = 0x1, count = 0x102 }, "
+      "TwoRelatedNumbersBe { id = 0x2, count = 0x204 }, TwoRelatedNumbersBe { id = 0x3, count = 0x306 }, "
+      "TwoRelatedNumbersBe { id = 0x4, count = 0x408 }] }",
+      view.ToString());
+}
+
+TEST(GeneratedPacketTest, testToStringOneFixedTypesStruct) {
+  StructWithFixedTypes swf;
+  swf.four_bits_ = FourBits::FIVE;
+  swf.id_ = 0x0d;
+  swf.array_ = {{0x01, 0x02, 0x03}};
+  swf.six_bytes_ = SixBytes{{0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6}};
+
+  auto packet = OneFixedTypesStructBuilder::Create(swf);
+  ASSERT_EQ(one_fixed_types_struct.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(one_fixed_types_struct.size(), packet_bytes->size());
+  for (size_t i = 0; i < one_fixed_types_struct.size(); i++) {
+    ASSERT_EQ(one_fixed_types_struct[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneFixedTypesStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+
+  ASSERT_EQ(
+      "OneFixedTypesStruct { one = StructWithFixedTypes { four_bits = FIVE, id = 0xd, array = ARRAY[0x1, 0x2, 0x3], "
+      "example_checksum = CHECKSUM, six_bytes = SixBytes } }",
+      view.ToString());
+}
+
 }  // namespace parser
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/parser/test/six_bytes.h b/gd/packet/parser/test/six_bytes.h
index 0f26324..04b0599 100644
--- a/gd/packet/parser/test/six_bytes.h
+++ b/gd/packet/parser/test/six_bytes.h
@@ -53,6 +53,9 @@
   bool operator!=(const SixBytes& rhs) const {
     return !(*this == rhs);
   }
+  std::string ToString() const {
+    return "SixBytes";
+  }
 };
 
 }  // namespace test
diff --git a/gd/packet/parser/test/test_packets.pdl b/gd/packet/parser/test/test_packets.pdl
index 2b612cc..43355ee 100644
--- a/gd/packet/parser/test/test_packets.pdl
+++ b/gd/packet/parser/test/test_packets.pdl
@@ -405,3 +405,38 @@
   seven : 56,
   eight : 64,
 }
+
+enum StructTypeNoZero : 4 {
+  TWO_BYTE = 0x02,
+  FOUR_BYTE = 0x04,
+  AT_LEAST_FOUR_BYTE = 0x05,
+}
+
+struct UnusedParentStructNoZero {
+  struct_type : StructTypeNoZero,
+  _reserved_ : 4,
+  length : 8,
+  _body_,
+}
+
+struct TwoByteStructNoZero : UnusedParentStructNoZero (struct_type = TWO_BYTE, length = 2) {
+  two_bytes : 16,
+}
+
+struct FourByteStructNoZero : UnusedParentStructNoZero (struct_type = FOUR_BYTE, length = 4) {
+  four_bytes : 32,
+}
+
+struct AtLeastFourByteStructNoZero : UnusedParentStructNoZero (struct_type = AT_LEAST_FOUR_BYTE) {
+  four_bytes : 32,
+  struct_type : StructTypeNoZero,
+  _body_,
+}
+
+struct EightByteStructNoZero : AtLeastFourByteStructNoZero (struct_type = FOUR_BYTE, length = 9) {
+  four_bytes : 32,
+}
+
+packet OneGenericStructArrayNoZero {
+  an_array : UnusedParentStructNoZero[],
+}
diff --git a/gd/packet/parser/test/variable.h b/gd/packet/parser/test/variable.h
index c452a37..920c14f 100644
--- a/gd/packet/parser/test/variable.h
+++ b/gd/packet/parser/test/variable.h
@@ -62,6 +62,10 @@
     *instance = ss.str();
     return it;
   }
+
+  std::string ToString() const {
+    return data;
+  }
 };
 
 }  // namespace test
diff --git a/gd/packet/python3_module.cc b/gd/packet/python3_module.cc
index 879732b..c7f3f57 100644
--- a/gd/packet/python3_module.cc
+++ b/gd/packet/python3_module.cc
@@ -19,6 +19,8 @@
 #include <pybind11/pybind11.h>
 #include <pybind11/stl.h>
 
+#include "hci/address.h"
+#include "hci/class_of_device.h"
 #include "packet/base_packet_builder.h"
 #include "packet/bit_inserter.h"
 #include "packet/iterator.h"
@@ -27,6 +29,7 @@
 #include "packet/packet_view.h"
 #include "packet/parser/checksum_type_checker.h"
 #include "packet/parser/custom_type_checker.h"
+#include "packet/raw_builder.h"
 
 namespace py = pybind11;
 
@@ -44,6 +47,8 @@
 
 namespace packet {
 
+using ::bluetooth::hci::Address;
+using ::bluetooth::hci::ClassOfDevice;
 using ::bluetooth::packet::BasePacketBuilder;
 using ::bluetooth::packet::BaseStruct;
 using ::bluetooth::packet::BitInserter;
@@ -53,31 +58,67 @@
 using ::bluetooth::packet::PacketBuilder;
 using ::bluetooth::packet::PacketStruct;
 using ::bluetooth::packet::PacketView;
+using ::bluetooth::packet::RawBuilder;
 using ::bluetooth::packet::parser::ChecksumTypeChecker;
 
 PYBIND11_MODULE(bluetooth_packets_python3, m) {
-  py::class_<BasePacketBuilder>(m, "BasePacketBuilder");
-  py::class_<PacketBuilder<kLittleEndian>, BasePacketBuilder>(m, "PacketBuilderLittleEndian");
-  py::class_<PacketBuilder<!kLittleEndian>, BasePacketBuilder>(m, "PacketBuilderBigEndian");
-  py::class_<BaseStruct>(m, "BaseStruct");
-  py::class_<PacketStruct<kLittleEndian>, BaseStruct>(m, "PacketStructLittleEndian");
-  py::class_<PacketStruct<!kLittleEndian>, BaseStruct>(m, "PacketStructBigEndian");
+  py::class_<BasePacketBuilder, std::shared_ptr<BasePacketBuilder>>(m, "BasePacketBuilder");
+  py::class_<RawBuilder, BasePacketBuilder, std::shared_ptr<RawBuilder>>(m, "RawBuilder")
+      .def(py::init([](std::vector<uint8_t> bytes) { return std::make_unique<RawBuilder>(bytes); }))
+      .def("Serialize", [](RawBuilder& builder) {
+        std::vector<uint8_t> packet;
+        BitInserter it(packet);
+        builder.Serialize(it);
+        std::string result = std::string(packet.begin(), packet.end());
+        return py::bytes(result);
+      });
+  py::class_<PacketBuilder<kLittleEndian>, BasePacketBuilder, std::shared_ptr<PacketBuilder<kLittleEndian>>>(
+      m, "PacketBuilderLittleEndian");
+  py::class_<PacketBuilder<!kLittleEndian>, BasePacketBuilder, std::shared_ptr<PacketBuilder<!kLittleEndian>>>(
+      m, "PacketBuilderBigEndian");
+  py::class_<BaseStruct, std::shared_ptr<BaseStruct>>(m, "BaseStruct");
+  py::class_<PacketStruct<kLittleEndian>, BaseStruct, std::shared_ptr<PacketStruct<kLittleEndian>>>(
+      m, "PacketStructLittleEndian");
+  py::class_<PacketStruct<!kLittleEndian>, BaseStruct, std::shared_ptr<PacketStruct<!kLittleEndian>>>(
+      m, "PacketStructBigEndian");
   py::class_<Iterator<kLittleEndian>>(m, "IteratorLittleEndian");
   py::class_<Iterator<!kLittleEndian>>(m, "IteratorBigEndian");
-  py::class_<PacketView<kLittleEndian>>(m, "PacketViewLittleEndian").def(py::init([](std::vector<uint8_t> bytes) {
-    // Make a copy
-    auto bytes_shared = std::make_shared<std::vector<uint8_t>>(bytes);
-    return std::make_unique<PacketView<kLittleEndian>>(bytes_shared);
-  }));
+  py::class_<PacketView<kLittleEndian>>(m, "PacketViewLittleEndian")
+      .def(py::init([](std::vector<uint8_t> bytes) {
+        // Make a copy
+        auto bytes_shared = std::make_shared<std::vector<uint8_t>>(bytes);
+        return std::make_unique<PacketView<kLittleEndian>>(bytes_shared);
+      }))
+      .def("GetBytes", [](const PacketView<kLittleEndian> view) {
+        std::string result;
+        for (auto it = view.begin(); it != view.end(); it++) {
+          result += *it;
+        }
+        return py::bytes(result);
+      });
   py::class_<PacketView<!kLittleEndian>>(m, "PacketViewBigEndian").def(py::init([](std::vector<uint8_t> bytes) {
     // Make a copy
     auto bytes_shared = std::make_shared<std::vector<uint8_t>>(bytes);
     return std::make_unique<PacketView<!kLittleEndian>>(bytes_shared);
   }));
 
-  bluetooth::hci::define_hci_packets_submodule(m);
-  bluetooth::l2cap::define_l2cap_packets_submodule(m);
-  bluetooth::security::define_smp_packets_submodule(m);
+  py::module hci_m = m.def_submodule("hci_packets", "A submodule of hci_packets");
+  bluetooth::hci::define_hci_packets_submodule(hci_m);
+
+  py::class_<Address>(hci_m, "Address")
+      .def(py::init<>())
+      .def("__repr__", [](const Address& a) { return a.ToString(); })
+      .def("__str__", [](const Address& a) { return a.ToString(); });
+
+  py::class_<ClassOfDevice>(hci_m, "ClassOfDevice")
+      .def(py::init<>())
+      .def("__repr__", [](const ClassOfDevice& c) { return c.ToString(); })
+      .def("__str__", [](const ClassOfDevice& c) { return c.ToString(); });
+
+  py::module l2cap_m = m.def_submodule("l2cap_packets", "A submodule of l2cap_packets");
+  bluetooth::l2cap::define_l2cap_packets_submodule(l2cap_m);
+  py::module security_m = m.def_submodule("security_packets", "A submodule of security_packets");
+  bluetooth::security::define_smp_packets_submodule(security_m);
 }
 
 }  // namespace packet
diff --git a/gd/proto/Android.bp b/gd/proto/Android.bp
new file mode 100644
index 0000000..7c27f1f
--- /dev/null
+++ b/gd/proto/Android.bp
@@ -0,0 +1,42 @@
+java_library_static {
+    name: "bluetooth-protos-lite",
+    host_supported: true,
+    proto: {
+        type: "lite",
+    },
+    srcs: [
+        "bluetooth/metrics/bluetooth.proto",
+        "bluetooth/bluetoothKeystore/keystore.proto",
+    ],
+}
+
+cc_library_static {
+    name: "libbt-protos-lite",
+    host_supported: true,
+    proto: {
+        export_proto_headers: true,
+        type: "lite",
+    },
+    srcs: [
+        "bluetooth/metrics/bluetooth.proto",
+        "bluetooth/bluetoothKeystore/keystore.proto",
+    ],
+}
+
+cc_library_static {
+    name: "libbluetooth-protos",
+    host_supported: true,
+    proto: {
+        export_proto_headers: true,
+        type: "lite",
+        include_dirs: ["external/protobuf/src"],
+    },
+    srcs: [
+        "bluetooth/metrics/bluetooth.proto",
+        "bluetooth/bluetoothKeystore/keystore.proto",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.bluetooth.updatable",
+    ],
+}
diff --git a/gd/proto/bluetooth/bluetoothKeystore/keystore.proto b/gd/proto/bluetooth/bluetoothKeystore/keystore.proto
new file mode 100644
index 0000000..d0e418b
--- /dev/null
+++ b/gd/proto/bluetooth/bluetoothKeystore/keystore.proto
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+// C++ namespace: bluetooth::metrics::BluetoothMetricsProto
+package bluetooth.keystore.BluetoothKeystoreProto;
+
+option java_package = "com.android.bluetooth";
+option java_outer_classname = "BluetoothKeystoreProto";
+
+// Holds encrypted, authenticated data.
+message EncryptedData {
+  // The initialization vector used during encryption.
+  optional bytes init_vector = 1;
+  // MAC of (init_vector + encrypted_data).
+  optional bytes authentication_data = 2;
+  optional bytes encrypted_data = 3;
+}
diff --git a/proto/bluetooth/metrics/bluetooth.proto b/gd/proto/bluetooth/metrics/bluetooth.proto
similarity index 95%
rename from proto/bluetooth/metrics/bluetooth.proto
rename to gd/proto/bluetooth/metrics/bluetooth.proto
index b1e28c3..6767987 100644
--- a/proto/bluetooth/metrics/bluetooth.proto
+++ b/gd/proto/bluetooth/metrics/bluetooth.proto
@@ -16,8 +16,6 @@
 
 syntax = "proto2";
 
-option optimize_for = LITE_RUNTIME;
-
 // C++ namespace: bluetooth::metrics::BluetoothMetricsProto
 package bluetooth.metrics.BluetoothMetricsProto;
 
@@ -181,8 +179,7 @@
   optional int32 disconnect_reason = 1;
 
   // Pair event time
-  optional int64 event_time_millis =
-      2;  // [(datapol.semantic_type) = ST_TIMESTAMP];
+  optional int64 event_time_millis = 2;  // [(datapol.semantic_type) = ST_TIMESTAMP];
 
   // The information about the device which it is paired to.
   optional DeviceInfo device_paired_with = 3;
@@ -209,8 +206,7 @@
   optional string name = 3;
 
   // Time of the event.
-  optional int64 event_time_millis =
-      4;  // [(datapol.semantic_type) = ST_TIMESTAMP];
+  optional int64 event_time_millis = 4;  // [(datapol.semantic_type) = ST_TIMESTAMP];
 }
 
 message ScanEvent {
@@ -247,8 +243,7 @@
   optional int32 number_results = 4;
 
   // Time of the event.
-  optional int64 event_time_millis =
-      5;  // [(datapol.semantic_type) = ST_TIMESTAMP];
+  optional int64 event_time_millis = 5;  // [(datapol.semantic_type) = ST_TIMESTAMP];
 }
 
 // Profile IDs defined in BluetoothProfile API class
diff --git a/gd/security/Android.bp b/gd/security/Android.bp
index 590558a..a9b81de 100644
--- a/gd/security/Android.bp
+++ b/gd/security/Android.bp
@@ -4,6 +4,8 @@
         "ecc/multprecision.cc",
         "ecc/p_256_ecc_pp.cc",
         "ecdh_keys.cc",
+        "facade_configuration_api.cc",
+        "l2cap_security_module_interface.cc",
         "pairing_handler_le.cc",
         "pairing_handler_le_legacy.cc",
         "pairing_handler_le_secure_connections.cc",
@@ -20,6 +22,7 @@
     srcs: [
         "ecc/multipoint_test.cc",
         "pairing_handler_le_unittest.cc",
+        "test/ecdh_keys_test.cc",
         "test/fake_l2cap_test.cc",
         "test/pairing_handler_le_pair_test.cc",
         ":BluetoothSecurityChannelTestSources",
diff --git a/gd/security/cert/cert_security.py b/gd/security/cert/cert_security.py
new file mode 100644
index 0000000..d611d26
--- /dev/null
+++ b/gd/security/cert/cert_security.py
@@ -0,0 +1,210 @@
+#!/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 logging
+
+from bluetooth_packets_python3 import hci_packets
+from cert.closable import safeClose
+from cert.event_stream import EventStream
+from cert.matchers import HciMatchers
+from cert.py_hci import PyHci
+from cert.py_security import PySecurity
+from cert.truth import assertThat
+from datetime import datetime
+from google.protobuf import empty_pb2 as empty_proto
+from hci.facade import facade_pb2 as hci_facade
+from l2cap.classic import facade_pb2 as l2cap_facade
+from security.facade_pb2 import IoCapabilities
+from security.facade_pb2 import AuthenticationRequirements
+from security.facade_pb2 import OobDataPresent
+
+
+class CertSecurity(PySecurity):
+    """
+        Contain all of the certification stack logic for sending and receiving
+        HCI commands following the Classic Pairing flows.
+    """
+    _io_cap_lookup = {
+        IoCapabilities.DISPLAY_ONLY: hci_packets.IoCapability.DISPLAY_ONLY,
+        IoCapabilities.DISPLAY_YES_NO_IO_CAP: hci_packets.IoCapability.DISPLAY_YES_NO,
+        IoCapabilities.KEYBOARD_ONLY: hci_packets.IoCapability.KEYBOARD_ONLY,
+        IoCapabilities.NO_INPUT_NO_OUTPUT: hci_packets.IoCapability.NO_INPUT_NO_OUTPUT,
+    }
+
+    _auth_req_lookup = {
+        AuthenticationRequirements.NO_BONDING:
+        hci_packets.AuthenticationRequirements.NO_BONDING,
+        AuthenticationRequirements.NO_BONDING_MITM_PROTECTION:
+        hci_packets.AuthenticationRequirements.NO_BONDING_MITM_PROTECTION,
+        AuthenticationRequirements.DEDICATED_BONDING:
+        hci_packets.AuthenticationRequirements.DEDICATED_BONDING,
+        AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION:
+        hci_packets.AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION,
+        AuthenticationRequirements.GENERAL_BONDING:
+        hci_packets.AuthenticationRequirements.GENERAL_BONDING,
+        AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION:
+        hci_packets.AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION,
+    }
+
+    _oob_present_lookup = {
+        OobDataPresent.NOT_PRESENT: hci_packets.OobDataPresent.NOT_PRESENT,
+        OobDataPresent.P192_PRESENT: hci_packets.OobDataPresent.P_192_PRESENT,
+        OobDataPresent.P256_PRESENT: hci_packets.OobDataPresent.P_256_PRESENT,
+        OobDataPresent.P192_AND_256_PRESENT: hci_packets.OobDataPresent.P_192_AND_256_PRESENT,
+    }
+
+    _hci_event_stream = None
+    _io_caps = hci_packets.IoCapability.DISPLAY_ONLY
+    _oob_data = hci_packets.OobDataPresent.NOT_PRESENT
+    _auth_reqs = hci_packets.AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION
+
+    _hci = None
+
+    def _enqueue_hci_command(self, command, expect_complete):
+        if (expect_complete):
+            self._hci.send_command_with_complete(command)
+        else:
+            self._hci.send_command_with_status(command)
+
+    def __init__(self, device):
+        """
+            Don't call super b/c the gRPC stream setup will crash test
+        """
+        logging.info("Cert: Init")
+        self._device = device
+        self._device.wait_channel_ready()
+        self._hci = PyHci(device)
+        self._hci.register_for_events(
+            hci_packets.EventCode.LINK_KEY_REQUEST, hci_packets.EventCode.IO_CAPABILITY_REQUEST,
+            hci_packets.EventCode.IO_CAPABILITY_RESPONSE, hci_packets.EventCode.USER_PASSKEY_NOTIFICATION,
+            hci_packets.EventCode.USER_PASSKEY_REQUEST, hci_packets.EventCode.USER_CONFIRMATION_REQUEST,
+            hci_packets.EventCode.REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION,
+            hci_packets.EventCode.LINK_KEY_NOTIFICATION, hci_packets.EventCode.SIMPLE_PAIRING_COMPLETE)
+        self._hci_event_stream = self._hci.get_event_stream()
+
+    def create_bond(self, address, type):
+        """
+            Creates a bond from the cert perspective
+        """
+        logging.info("Cert: Creating bond to '%s' from '%s'" % (str(address), str(self._device.address)))
+        # TODO(optedoblivion): Trigger connection to Send AuthenticationRequested
+
+    def remove_bond(self, address, type):
+        """
+            We store the link key locally in the test and pretend
+            So to remove_bond we need to Remove the "stored" data
+        """
+        pass
+
+    def set_io_capabilities(self, io_capabilities):
+        """
+            Set the IO Capabilities used for the cert
+        """
+        logging.info("Cert: setting IO Capabilities data to '%s'" % self._io_capabilities_name_lookup.get(
+            io_capabilities, "ERROR"))
+        self._io_caps = self._io_cap_lookup.get(io_capabilities, hci_packets.IoCapability.DISPLAY_YES_NO)
+
+    def set_authentication_requirements(self, auth_reqs):
+        """
+            Establish authentication requirements for the stack
+        """
+        logging.info("Cert: setting Authentication Requirements data to '%s'" % self._auth_reqs_name_lookup.get(
+            auth_reqs, "ERROR"))
+        self._auth_reqs = self._auth_req_lookup.get(auth_reqs, hci_packets.AuthenticationRequirements.GENERAL_BONDING)
+
+    def set_oob_data(self, data):
+        """
+            Set the Out-of-band data for SSP pairing
+        """
+        logging.info("Cert: setting OOB data present to '%s'" % data)
+        self._oob_data = self._oob_present_lookup.get(data, hci_packets.OobDataPresent.NOT_PRESENT)
+
+    def send_ui_callback(self, address, callback_type, b, uid):
+        """
+            Pretend to answer the pairing dailog as a user
+        """
+        logging.info("Cert: Send user input callback uid:%d; response: %s" % (uid, b))
+        # TODO(optedoblivion): Make callback and set it to the module
+
+    def enable_secure_simple_pairing(self):
+        """
+            This is called when you want to enable SSP for testing
+        """
+        logging.info("Cert: Sending WRITE_SIMPLE_PAIRING_MODE [True]")
+        self._enqueue_hci_command(hci_packets.WriteSimplePairingModeBuilder(hci_packets.Enable.ENABLED), True)
+        logging.info("Cert: Waiting for controller response")
+        assertThat(self._hci_event_stream).emits(lambda msg: b'\x0e\x04\x01\x56\x0c' in msg.event)
+
+    def accept_pairing(self, dut_address, reply_boolean):
+        """
+            Here we handle the pairing events at the HCI level
+        """
+        logging.info("Cert: Waiting for LINK_KEY_REQUEST")
+        assertThat(self._hci_event_stream).emits(HciMatchers.LinkKeyRequest())
+        logging.info("Cert: Sending LINK_KEY_REQUEST_NEGATIVE_REPLY")
+        self._enqueue_hci_command(hci_packets.LinkKeyRequestNegativeReplyBuilder(dut_address.decode('utf8')), True)
+        logging.info("Cert: Waiting for IO_CAPABILITY_REQUEST")
+        assertThat(self._hci_event_stream).emits(HciMatchers.IoCapabilityRequest())
+        logging.info("Cert: Sending IO_CAPABILITY_REQUEST_REPLY")
+        self._enqueue_hci_command(
+            hci_packets.IoCapabilityRequestReplyBuilder(
+                dut_address.decode('utf8'), self._io_caps, self._oob_data, self._auth_reqs), True)
+        logging.info("Cert: Waiting for USER_CONFIRMATION_REQUEST")
+        assertThat(self._hci_event_stream).emits(HciMatchers.UserConfirmationRequest())
+        logging.info("Cert: Sending Simulated User Response '%s'" % reply_boolean)
+        if reply_boolean:
+            logging.info("Cert: Sending USER_CONFIRMATION_REQUEST_REPLY")
+            self._enqueue_hci_command(hci_packets.UserConfirmationRequestReplyBuilder(dut_address.decode('utf8')), True)
+            logging.info("Cert: Waiting for SIMPLE_PAIRING_COMPLETE")
+            assertThat(self._hci_event_stream).emits(HciMatchers.SimplePairingComplete())
+            logging.info("Cert: Waiting for LINK_KEY_NOTIFICATION")
+            assertThat(self._hci_event_stream).emits(HciMatchers.LinkKeyNotification())
+        else:
+            logging.info("Cert: Sending USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY")
+            self._enqueue_hci_command(
+                hci_packets.UserConfirmationRequestNegativeReplyBuilder(dut_address.decode('utf8')), True)
+            logging.info("Cert: Waiting for SIMPLE_PAIRING_COMPLETE")
+            assertThat(self._hci_event_stream).emits(HciMatchers.SimplePairingComplete())
+
+    def on_user_input(self, dut_address, reply_boolean, expected_ui_event):
+        """
+            Cert doesn't need the test to respond to the ui event
+            Cert responds in accept pairing
+        """
+        pass
+
+    def wait_for_bond_event(self, expected_bond_event):
+        """
+            A bond event will be triggered once the bond process
+            is complete.  For the DUT we need to wait for it,
+            for Cert it isn't needed.
+        """
+        pass
+
+    def enforce_security_policy(self, address, type, policy):
+        """
+            Pass for now
+        """
+        pass
+
+    def wait_for_enforce_security_event(self, expected_enforce_security_event):
+        """
+            Cert side needs to pass
+        """
+        pass
+
+    def close(self):
+        safeClose(self._hci)
diff --git a/gd/security/cert/le_security_test.py b/gd/security/cert/le_security_test.py
new file mode 100644
index 0000000..7b7d638
--- /dev/null
+++ b/gd/security/cert/le_security_test.py
@@ -0,0 +1,637 @@
+#
+#   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 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 OobDataPresent
+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 bluetooth_packets_python3.hci_packets import OpCode
+
+LeIoCapabilities = LeIoCapabilityMessage.LeIoCapabilities
+
+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 = OobDataMessage(data_present=OobDataPresent.NOT_PRESENT)
+
+
+class LeSecurityTest(GdBaseTestClass):
+    """
+        Collection of tests that each sample results from
+        different (unique) combinations of io capabilities, authentication requirements, and oob data.
+    """
+
+    def setup_class(self):
+        super().setup_class(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)
+
+        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)
+        self.cert_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'C5:11:FF:AA:33:22')), type=common.RANDOM_DEVICE_ADDRESS)
+        cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.cert_address)
+        self.cert.security.SetLeInitiatorAddressPolicy(cert_privacy_policy)
+
+    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,
+            event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            address_type=common.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)
+
+    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,
+            event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            address_type=common.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)
+
+    @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), 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), 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), 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), 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 master, initiator when both sides do not require MITM protection.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+
+        self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.cert_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))
+
+    @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 slave, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.dut_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))
+
+    @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 slave, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.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))
+
+    @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 master, initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.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))
+
+    @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.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.cert_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))
+
+    @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.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.dut_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))
+
+    @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 slave, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.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))
+
+    @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 master, initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.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))
+
+    @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 master, initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.cert_address))
+
+        assertThat(self.cert_security.get_ui_stream()).emits(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY))
+
+        # 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), 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 slave, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.dut_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))
+
+        # 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), 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 slave, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.dut_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.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), 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 master, initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.cert.security.SetOobDataPresent(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))
+
+        # 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.cert_address))
+
+        assertThat(self.cert_security.get_ui_stream()).emits(SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY))
+
+        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), timeout=timedelta(seconds=10))
diff --git a/gd/security/cert/security_test.py b/gd/security/cert/security_test.py
new file mode 100644
index 0000000..97bfc40
--- /dev/null
+++ b/gd/security/cert/security_test.py
@@ -0,0 +1,277 @@
+#
+#   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.gd_base_test import GdBaseTestClass
+from cert.py_security import PySecurity
+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 SecurityTest(GdBaseTestClass):
+    """
+        Collection of tests that each sample results from 
+        different (unique) combinations of io capabilities, authentication requirements, and oob data.
+    """
+
+    # 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,
+        # TODO(optedoblivion): Figure out MITM cases
+        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"
+    )
+
+    def setup_class(self):
+        super().setup_class(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)
+
+    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 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_same_acl(self):
+        # Arrange
+        self.dut_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
+        self.dut_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION)
+        self.dut_security.set_oob_data(OobDataPresent.NOT_PRESENT)
+        self.cert_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
+        self.cert_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION)
+        self.cert_security.set_oob_data(OobDataPresent.NOT_PRESENT)
+
+        # 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.AUTHENTICATED_ENCRYPTED_TRANSPORT)
+
+        self._verify_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.wait_for_enforce_security_event(expected_enforce_security_event=False)
+
+    # 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_MITM_PROTECTION)
+        self.dut_security.set_oob_data(OobDataPresent.NOT_PRESENT)
+        self.cert_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
+        self.cert_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION)
+        self.cert_security.set_oob_data(OobDataPresent.NOT_PRESENT)
+
+        # 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)
+
+        # Give time for ACL to disconnect
+        time.sleep(1)
+
+        # 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)
+
+    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 init_io_capability in self.io_capabilities:
+            for init_auth_reqs in self.auth_reqs:
+                for init_oob_present in self.oob_present:
+                    for resp_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: %d ; %d ; %d " % (init_io_capability, init_auth_reqs,
+                                                                                 init_oob_present))
+                                logging.info("CERT Test Config: %d ; %d ; %d " % (resp_io_capability, cert_auth_reqs,
+                                                                                  cert_oob_present))
+                                logging.info("===================================================")
+                                logging.info("")
+                                self.dut_security.set_io_capabilities(init_io_capability)
+                                self.dut_security.set_authentication_requirements(init_auth_reqs)
+                                self.dut_security.set_oob_data(init_oob_present)
+                                self.cert_security.set_io_capabilities(resp_io_capability)
+                                self.cert_security.set_authentication_requirements(cert_auth_reqs)
+                                self.cert_security.set_oob_data(cert_oob_present)
+                                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 init_io_capability == IoCapabilities.DISPLAY_ONLY:
+                                    if resp_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_YES_NO_WITH_VALUE
+                                    elif resp_io_capability == IoCapabilities.KEYBOARD_ONLY:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                elif init_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                    expected_init_ui_event = UiMsgType.DISPLAY_YES_NO_WITH_VALUE
+                                    if resp_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_YES_NO_WITH_VALUE
+                                    elif resp_io_capability == IoCapabilities.KEYBOARD_ONLY:
+                                        expected_init_ui_event = UiMsgType.DISPLAY_PASSKEY
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                    elif resp_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
+                                        expected_init_ui_event = UiMsgType.DISPLAY_YES_NO  # No value
+                                elif init_io_capability == IoCapabilities.KEYBOARD_ONLY:
+                                    expected_init_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                    if resp_io_capability == IoCapabilities.DISPLAY_ONLY:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY
+                                    elif resp_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                    elif resp_io_capability == IoCapabilities.KEYBOARD_ONLY:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                elif init_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
+                                    if resp_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_YES_NO  # No value
+
+                                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)
+
+                                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)
+
+                                time.sleep(.1)
diff --git a/gd/security/channel/security_manager_channel.cc b/gd/security/channel/security_manager_channel.cc
index fd6e9df..767a0e1 100644
--- a/gd/security/channel/security_manager_channel.cc
+++ b/gd/security/channel/security_manager_channel.cc
@@ -17,21 +17,65 @@
  */
 #include "security/channel/security_manager_channel.h"
 
+#include "hci/address.h"
 #include "security/smp_packets.h"
 
 namespace bluetooth {
 namespace security {
 namespace channel {
 
+/**
+ * Main Constructor
+ */
+SecurityManagerChannel::SecurityManagerChannel(os::Handler* handler, hci::HciLayer* hci_layer)
+    : listener_(nullptr),
+      hci_security_interface_(
+          hci_layer->GetSecurityInterface(handler->BindOn(this, &SecurityManagerChannel::OnHciEventReceived))),
+      handler_(handler),
+      l2cap_security_interface_(nullptr) {}
+
+SecurityManagerChannel::~SecurityManagerChannel() {
+  l2cap_security_interface_->Unregister();
+  l2cap_security_interface_ = nullptr;
+}
+
+void SecurityManagerChannel::Connect(hci::Address address) {
+  ASSERT_LOG(l2cap_security_interface_ != nullptr, "L2cap Security Interface is null!");
+  auto entry = link_map_.find(address);
+  if (entry != link_map_.end()) {
+    LOG_WARN("Already connected to '%s'", address.ToString().c_str());
+    entry->second->Hold();
+    entry->second->EnsureAuthenticated();
+    return;
+  }
+  l2cap_security_interface_->InitiateConnectionForSecurity(address);
+}
+
+void SecurityManagerChannel::Release(hci::Address address) {
+  auto entry = link_map_.find(address);
+  if (entry == link_map_.end()) {
+    LOG_WARN("Unknown address '%s'", address.ToString().c_str());
+    return;
+  }
+  entry->second->Release();
+}
+
+void SecurityManagerChannel::Disconnect(hci::Address address) {
+  auto entry = link_map_.find(address);
+  if (entry == link_map_.end()) {
+    LOG_WARN("Unknown address '%s'", address.ToString().c_str());
+    return;
+  }
+  entry->second->Disconnect();
+}
+
 void SecurityManagerChannel::OnCommandComplete(hci::CommandCompleteView packet) {
-  ASSERT(packet.IsValid());
-  // TODO(optedoblivion): Verify HCI commands
+  ASSERT_LOG(packet.IsValid(), "Bad command response");
 }
 
 void SecurityManagerChannel::SendCommand(std::unique_ptr<hci::SecurityCommandBuilder> command) {
-  hci_security_interface_->EnqueueCommand(
-      std::move(command), common::BindOnce(&SecurityManagerChannel::OnCommandComplete, common::Unretained(this)),
-      handler_);
+  hci_security_interface_->EnqueueCommand(std::move(command),
+                                          handler_->BindOnceOn(this, &SecurityManagerChannel::OnCommandComplete));
 }
 
 void SecurityManagerChannel::OnHciEventReceived(hci::EventPacketView packet) {
@@ -40,6 +84,25 @@
   listener_->OnHciEventReceived(packet);
 }
 
+void SecurityManagerChannel::OnLinkConnected(std::unique_ptr<l2cap::classic::LinkSecurityInterface> link) {
+  // Multiple links possible?
+  link->Hold();
+  link->EnsureAuthenticated();
+  link_map_.emplace(link->GetRemoteAddress(), std::move(link));
+}
+
+void SecurityManagerChannel::OnLinkDisconnected(hci::Address address) {
+  auto entry = link_map_.find(address);
+  if (entry == link_map_.end()) {
+    LOG_WARN("Unknown address '%s'", address.ToString().c_str());
+    return;
+  }
+  entry->second.reset();
+  link_map_.erase(entry);
+  ASSERT_LOG(listener_ != nullptr, "Set listener!");
+  listener_->OnConnectionClosed(address);
+}
+
 }  // namespace channel
 }  // namespace security
 }  // namespace bluetooth
diff --git a/gd/security/channel/security_manager_channel.h b/gd/security/channel/security_manager_channel.h
index e844a4c..8746115 100644
--- a/gd/security/channel/security_manager_channel.h
+++ b/gd/security/channel/security_manager_channel.h
@@ -18,12 +18,15 @@
 #pragma once
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "hci/address_with_type.h"
 #include "hci/hci_layer.h"
 #include "hci/hci_packets.h"
 #include "hci/security_interface.h"
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/classic/link_security_interface.h"
 
 namespace bluetooth {
 namespace security {
@@ -36,21 +39,42 @@
  public:
   virtual ~ISecurityManagerChannelListener() = default;
   virtual void OnHciEventReceived(hci::EventPacketView packet) = 0;
+  virtual void OnConnectionClosed(hci::Address) = 0;
 };
 
 /**
  * Channel for consolidating traffic and making the transport agnostic.
  */
-class SecurityManagerChannel {
+class SecurityManagerChannel : public l2cap::classic::LinkSecurityInterfaceListener {
  public:
-  explicit SecurityManagerChannel(os::Handler* handler, hci::HciLayer* hci_layer)
-      : listener_(nullptr),
-        hci_security_interface_(hci_layer->GetSecurityInterface(
-            common::Bind(&SecurityManagerChannel::OnHciEventReceived, common::Unretained(this)), handler)),
-        handler_(handler) {}
-  ~SecurityManagerChannel() {
-    delete listener_;
-  }
+  SecurityManagerChannel(os::Handler* handler, hci::HciLayer* hci_layer);
+
+  virtual ~SecurityManagerChannel();
+
+  /**
+   * Creates a connection to the device which triggers pairing
+   *
+   * @param address remote address of device to pair with
+   */
+  void Connect(hci::Address address);
+
+  /**
+   * Releases link hold so it can disconnect as normally
+   *
+   * i.e. signals we no longer need this if acl manager wants to clean it up
+   *
+   * @param address remote address to disconnect
+   */
+  void Release(hci::Address address);
+
+  /**
+   * Immediately disconnects currently connected channel
+   *
+   * i.e. force disconnect
+   *
+   * @param address remote address to disconnect
+   */
+  void Disconnect(hci::Address address);
 
   /**
    * Send a given SMP command over the SecurityManagerChannel
@@ -68,6 +92,10 @@
     listener_ = listener;
   }
 
+  void SetSecurityInterface(l2cap::classic::SecurityInterface* security_interface) {
+    l2cap_security_interface_ = security_interface;
+  }
+
   /**
    * Called when an incoming HCI event happens
    *
@@ -82,10 +110,16 @@
    */
   void OnCommandComplete(hci::CommandCompleteView packet);
 
+  // Interface overrides
+  void OnLinkConnected(std::unique_ptr<l2cap::classic::LinkSecurityInterface> link) override;
+  void OnLinkDisconnected(hci::Address address) override;
+
  private:
-  ISecurityManagerChannelListener* listener_;
-  hci::SecurityInterface* hci_security_interface_;
-  os::Handler* handler_;
+  ISecurityManagerChannelListener* listener_{nullptr};
+  hci::SecurityInterface* hci_security_interface_{nullptr};
+  os::Handler* handler_{nullptr};
+  l2cap::classic::SecurityInterface* l2cap_security_interface_{nullptr};
+  std::unordered_map<hci::Address, std::unique_ptr<l2cap::classic::LinkSecurityInterface>> link_map_;
 };
 
 }  // namespace channel
diff --git a/gd/security/channel/security_manager_channel_unittest.cc b/gd/security/channel/security_manager_channel_unittest.cc
index ee12b92..f6fefa3 100644
--- a/gd/security/channel/security_manager_channel_unittest.cc
+++ b/gd/security/channel/security_manager_channel_unittest.cc
@@ -15,14 +15,16 @@
  *  limitations under the License.
  *
  */
-#include "security_manager_channel.h"
+#include "security/channel/security_manager_channel.h"
 
 #include <gtest/gtest.h>
 
+#include "hci/address.h"
 #include "hci/hci_packets.h"
 #include "packet/raw_builder.h"
 #include "security/smp_packets.h"
 #include "security/test/fake_hci_layer.h"
+#include "security/test/fake_security_interface.h"
 
 namespace bluetooth {
 namespace security {
@@ -41,6 +43,24 @@
 using os::Thread;
 using packet::RawBuilder;
 
+static bool on_link_connected_called = false;
+static bool on_link_disconnected_called = false;
+
+class FakeSecurityManagerChannel : public SecurityManagerChannel {
+ public:
+  FakeSecurityManagerChannel(os::Handler* handler, hci::HciLayer* hci_layer)
+      : SecurityManagerChannel(handler, hci_layer) {}
+  ~FakeSecurityManagerChannel() {}
+
+  void OnLinkConnected(std::unique_ptr<l2cap::classic::LinkSecurityInterface> link) {
+    on_link_connected_called = true;
+  }
+
+  void OnLinkDisconnected(hci::Address address) {
+    on_link_disconnected_called = true;
+  }
+};
+
 class SecurityManagerChannelCallback : public ISecurityManagerChannelListener {
  public:
   // HCI
@@ -184,34 +204,51 @@
         break;
     }
   }
+
+  void OnConnectionClosed(hci::Address address) override {
+    LOG_DEBUG("Called");
+  }
 };
 
 class SecurityManagerChannelTest : public ::testing::Test {
  protected:
   void SetUp() override {
+    hci::Address address;
+    hci::Address::FromString("01:23:45:67:89:AB:CD", address);
+    device_ = hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS);
+    on_link_connected_called = false;
+    on_link_disconnected_called = false;
     handler_ = new Handler(&thread_);
     callback_ = new SecurityManagerChannelCallback();
     hci_layer_ = new FakeHciLayer();
     fake_registry_.InjectTestModule(&FakeHciLayer::Factory, hci_layer_);
     fake_registry_.Start<FakeHciLayer>(&thread_);
-    channel_ = new SecurityManagerChannel(handler_, hci_layer_);
+    channel_ = new FakeSecurityManagerChannel(handler_, hci_layer_);
     channel_->SetChannelListener(callback_);
+    security_interface_ = new FakeSecurityInterface(handler_, channel_);
+    channel_->SetSecurityInterface(security_interface_);
   }
 
   void TearDown() override {
     channel_->SetChannelListener(nullptr);
     handler_->Clear();
-    fake_registry_.SynchronizeModuleHandler(&FakeHciLayer::Factory, std::chrono::milliseconds(20));
+    synchronize();
     fake_registry_.StopAll();
     delete handler_;
     delete channel_;
     delete callback_;
+    delete security_interface_;
+  }
+
+  void synchronize() {
+    fake_registry_.SynchronizeModuleHandler(&FakeHciLayer::Factory, std::chrono::milliseconds(20));
   }
 
   TestModuleRegistry fake_registry_;
   Thread& thread_ = fake_registry_.GetTestThread();
   Handler* handler_ = nullptr;
   FakeHciLayer* hci_layer_ = nullptr;
+  l2cap::classic::SecurityInterface* security_interface_ = nullptr;
   SecurityManagerChannel* channel_ = nullptr;
   SecurityManagerChannelCallback* callback_ = nullptr;
   hci::AddressWithType device_;
@@ -221,6 +258,7 @@
 
 TEST_F(SecurityManagerChannelTest, recv_io_cap_request) {
   hci_layer_->IncomingEvent(hci::IoCapabilityRequestBuilder::Create(device_.GetAddress()));
+  synchronize();
   ASSERT_TRUE(callback_->receivedIoCapabilityRequest);
 }
 
@@ -265,11 +303,13 @@
   AuthenticationRequirements authentication_requirements = (AuthenticationRequirements)0x00;
   hci_layer_->IncomingEvent(hci::IoCapabilityResponseBuilder::Create(device_.GetAddress(), io_capability, oob_present,
                                                                      authentication_requirements));
+  synchronize();
   ASSERT_TRUE(callback_->receivedIoCapabilityResponse);
 }
 
 TEST_F(SecurityManagerChannelTest, recv_pin_code_request) {
   hci_layer_->IncomingEvent(hci::PinCodeRequestBuilder::Create(device_.GetAddress()));
+  synchronize();
   ASSERT_TRUE(callback_->receivedPinCodeRequest);
 }
 
@@ -308,12 +348,14 @@
 TEST_F(SecurityManagerChannelTest, recv_user_passkey_notification) {
   uint32_t passkey = 0x00;
   hci_layer_->IncomingEvent(hci::UserPasskeyNotificationBuilder::Create(device_.GetAddress(), passkey));
+  synchronize();
   ASSERT_TRUE(callback_->receivedUserPasskeyNotification);
 }
 
 TEST_F(SecurityManagerChannelTest, recv_user_confirmation_request) {
   uint32_t numeric_value = 0x0;
   hci_layer_->IncomingEvent(hci::UserConfirmationRequestBuilder::Create(device_.GetAddress(), numeric_value));
+  synchronize();
   ASSERT_TRUE(callback_->receivedUserConfirmationRequest);
 }
 
@@ -349,6 +391,7 @@
 
 TEST_F(SecurityManagerChannelTest, recv_remote_oob_data_request) {
   hci_layer_->IncomingEvent(hci::RemoteOobDataRequestBuilder::Create(device_.GetAddress()));
+  synchronize();
   ASSERT_TRUE(callback_->receivedRemoteOobDataRequest);
 }
 
@@ -416,6 +459,7 @@
 
 TEST_F(SecurityManagerChannelTest, recv_link_key_request) {
   hci_layer_->IncomingEvent(hci::LinkKeyRequestBuilder::Create(device_.GetAddress()));
+  synchronize();
   ASSERT_TRUE(callback_->receivedLinkKeyRequest);
 }
 
@@ -423,6 +467,7 @@
   std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
   hci_layer_->IncomingEvent(
       hci::LinkKeyNotificationBuilder::Create(device_.GetAddress(), link_key, hci::KeyType::DEBUG_COMBINATION));
+  synchronize();
   ASSERT_TRUE(callback_->receivedLinkKeyNotification);
 }
 
@@ -430,6 +475,7 @@
   uint16_t connection_handle = 0x0;
   hci_layer_->IncomingEvent(
       hci::MasterLinkKeyCompleteBuilder::Create(hci::ErrorCode::SUCCESS, connection_handle, hci::KeyFlag::TEMPORARY));
+  synchronize();
   ASSERT_TRUE(callback_->receivedMasterLinkKeyComplete);
 }
 
@@ -437,12 +483,14 @@
   uint16_t connection_handle = 0x0;
   hci_layer_->IncomingEvent(
       hci::ChangeConnectionLinkKeyCompleteBuilder::Create(hci::ErrorCode::SUCCESS, connection_handle));
+  synchronize();
   ASSERT_TRUE(callback_->receivedChangeConnectionLinkKeyComplete);
 }
 
 TEST_F(SecurityManagerChannelTest, recv_return_link_keys) {
   std::vector<hci::ZeroKeyAndAddress> keys;
   hci_layer_->IncomingEvent(hci::ReturnLinkKeysBuilder::Create(keys));
+  synchronize();
   ASSERT_TRUE(callback_->receivedReturnLinkKeys);
 }
 
@@ -528,6 +576,7 @@
   uint16_t connection_handle = 0x0;
   hci_layer_->IncomingEvent(
       hci::EncryptionChangeBuilder::Create(hci::ErrorCode::SUCCESS, connection_handle, hci::EncryptionEnabled::ON));
+  synchronize();
   ASSERT_TRUE(callback_->receivedEncryptionChange);
 }
 
@@ -535,6 +584,7 @@
   uint16_t connection_handle = 0x0;
   hci_layer_->IncomingEvent(
       hci::EncryptionKeyRefreshCompleteBuilder::Create(hci::ErrorCode::SUCCESS, connection_handle));
+  synchronize();
   ASSERT_TRUE(callback_->receivedEncryptionKeyRefreshComplete);
 }
 
@@ -572,6 +622,7 @@
 
 TEST_F(SecurityManagerChannelTest, recv_simple_pairing_complete) {
   hci_layer_->IncomingEvent(hci::SimplePairingCompleteBuilder::Create(hci::ErrorCode::SUCCESS, device_.GetAddress()));
+  synchronize();
   ASSERT_TRUE(callback_->receivedSimplePairingComplete);
 }
 
@@ -608,6 +659,7 @@
 TEST_F(SecurityManagerChannelTest, recv_keypress_notification) {
   hci_layer_->IncomingEvent(
       hci::KeypressNotificationBuilder::Create(device_.GetAddress(), hci::KeypressNotificationType::ENTRY_COMPLETED));
+  synchronize();
   ASSERT_TRUE(callback_->receivedKeypressNotification);
 }
 
@@ -629,9 +681,20 @@
 
 TEST_F(SecurityManagerChannelTest, recv_user_passkey_request) {
   hci_layer_->IncomingEvent(hci::UserPasskeyRequestBuilder::Create(device_.GetAddress()));
+  synchronize();
   ASSERT_TRUE(callback_->receivedUserPasskeyRequest);
 }
 
+TEST_F(SecurityManagerChannelTest, test_l2cap_security_interface_api) {
+  ASSERT_FALSE(on_link_connected_called);
+  channel_->Connect(device_.GetAddress());
+  ASSERT_TRUE(on_link_connected_called);
+  ASSERT_FALSE(on_link_disconnected_called);
+  channel_->Release(device_.GetAddress());
+  // TODO(optedoblivion): Lock and wait
+  // ASSERT_TRUE(on_link_disconnected_called);
+}
+
 }  // namespace
 }  // namespace channel
 }  // namespace security
diff --git a/gd/security/ecc/p_256_ecc_pp.h b/gd/security/ecc/p_256_ecc_pp.h
index 93af7ee..54c7245 100644
--- a/gd/security/ecc/p_256_ecc_pp.h
+++ b/gd/security/ecc/p_256_ecc_pp.h
@@ -60,7 +60,8 @@
     .omega = {0},
 
     .G = {.x = {0xd898c296, 0xf4a13945, 0x2deb33a0, 0x77037d81, 0x63a440f2, 0xf8bce6e5, 0xe12c4247, 0x6b17d1f2},
-          .y = {0x37bf51f5, 0xcbb64068, 0x6b315ece, 0x2bce3357, 0x7c0f9e16, 0x8ee7eb4a, 0xfe1a7f9b, 0x4fe342e2}},
+          .y = {0x37bf51f5, 0xcbb64068, 0x6b315ece, 0x2bce3357, 0x7c0f9e16, 0x8ee7eb4a, 0xfe1a7f9b, 0x4fe342e2},
+          .z = {0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}},
 };
 
 /* This function checks that point is on the elliptic curve*/
diff --git a/gd/security/ecdh_keys.cc b/gd/security/ecdh_keys.cc
index fa3d794..951654c 100644
--- a/gd/security/ecdh_keys.cc
+++ b/gd/security/ecdh_keys.cc
@@ -28,11 +28,18 @@
 #include "security/ecc/p_256_ecc_pp.h"
 
 namespace {
+
+static bool srand_initiated = false;
+
 template <size_t SIZE>
 static std::array<uint8_t, SIZE> GenerateRandom() {
-  // TODO:  We need a proper  random number generator here.
-  // use current time as seed for random generator
-  std::srand(std::time(nullptr));
+  if (!srand_initiated) {
+    srand_initiated = true;
+    // TODO:  We need a proper  random number generator here.
+    // use current time as seed for random generator
+    std::srand(std::time(nullptr));
+  }
+
   std::array<uint8_t, SIZE> r;
   for (size_t i = 0; i < SIZE; i++) r[i] = std::rand();
   return r;
@@ -45,9 +52,10 @@
 
 std::pair<std::array<uint8_t, 32>, EcdhPublicKey> GenerateECDHKeyPair() {
   std::array<uint8_t, 32> private_key = GenerateRandom<32>();
+  std::array<uint8_t, 32> private_key_copy = private_key;
   ecc::Point public_key;
 
-  ECC_PointMult(&public_key, &(ecc::curve_p256.G), (uint32_t*)private_key.data());
+  ECC_PointMult(&public_key, &(ecc::curve_p256.G), (uint32_t*)private_key_copy.data());
 
   EcdhPublicKey pk;
   memcpy(pk.x.data(), public_key.x, 32);
@@ -71,6 +79,9 @@
   memcpy(private_key, my_private_key.data(), 32);
   memcpy(peer_publ_key.x, remote_public_key.x.data(), 32);
   memcpy(peer_publ_key.y, remote_public_key.y.data(), 32);
+  memset(peer_publ_key.z, 0, 32);
+  peer_publ_key.z[0] = 1;
+
   ECC_PointMult(&new_publ_key, &peer_publ_key, (uint32_t*)private_key);
 
   std::array<uint8_t, 32> dhkey;
diff --git a/gd/security/facade.cc b/gd/security/facade.cc
index 2f772fa..8207219 100644
--- a/gd/security/facade.cc
+++ b/gd/security/facade.cc
@@ -14,24 +14,36 @@
  * limitations under the License.
  */
 #include "security/facade.h"
-#include "hci/hci_layer.h"
-#include "l2cap/classic/l2cap_classic_module.h"
-#include "l2cap/le/l2cap_le_module.h"
+
+#include "grpc/grpc_event_queue.h"
+#include "hci/address_with_type.h"
+#include "hci/le_address_manager.h"
+#include "l2cap/classic/security_policy.h"
 #include "os/handler.h"
 #include "security/facade.grpc.pb.h"
+#include "security/security_manager_listener.h"
 #include "security/security_module.h"
+#include "security/ui.h"
 
 namespace bluetooth {
 namespace security {
 
-class SecurityModuleFacadeService : public SecurityModuleFacade::Service, public ISecurityManagerListener {
+namespace {
+constexpr uint8_t AUTH_REQ_NO_BOND = 0x01;
+constexpr uint8_t AUTH_REQ_BOND = 0x01;
+constexpr uint8_t AUTH_REQ_MITM_MASK = 0x04;
+constexpr uint8_t AUTH_REQ_SECURE_CONNECTIONS_MASK = 0x08;
+constexpr uint8_t AUTH_REQ_KEYPRESS_MASK = 0x10;
+constexpr uint8_t AUTH_REQ_CT2_MASK = 0x20;
+constexpr uint8_t AUTH_REQ_RFU_MASK = 0xC0;
+}  // namespace
+
+class SecurityModuleFacadeService : public SecurityModuleFacade::Service, public ISecurityManagerListener, public UI {
  public:
-  SecurityModuleFacadeService(SecurityModule* security_module, l2cap::le::L2capLeModule* l2cap_le_module,
-                              l2cap::classic::L2capClassicModule* l2cap_classic_module, hci::HciLayer* hci_layer,
-                              ::bluetooth::os::Handler* security_handler)
-      : security_module_(security_module), l2cap_le_module_(l2cap_le_module),
-        l2cap_classic_module_(l2cap_classic_module), security_handler_(security_handler) {
+  SecurityModuleFacadeService(SecurityModule* security_module, ::bluetooth::os::Handler* security_handler)
+      : security_module_(security_module), security_handler_(security_handler) {
     security_module_->GetSecurityManager()->RegisterCallbackListener(this, security_handler_);
+    security_module_->GetSecurityManager()->SetUserInterfaceHandler(this, security_handler_);
   }
 
   ::grpc::Status CreateBond(::grpc::ServerContext* context, const facade::BluetoothAddressWithType* request,
@@ -43,6 +55,15 @@
     return ::grpc::Status::OK;
   }
 
+  ::grpc::Status CreateBondLe(::grpc::ServerContext* context, const facade::BluetoothAddressWithType* request,
+                              ::google::protobuf::Empty* response) override {
+    hci::Address peer;
+    ASSERT(hci::Address::FromString(request->address().address(), peer));
+    hci::AddressType peer_type = static_cast<hci::AddressType>(request->type());
+    security_module_->GetSecurityManager()->CreateBondLe(hci::AddressWithType(peer, peer_type));
+    return ::grpc::Status::OK;
+  }
+
   ::grpc::Status CancelBond(::grpc::ServerContext* context, const facade::BluetoothAddressWithType* request,
                             ::google::protobuf::Empty* response) override {
     hci::Address peer;
@@ -61,32 +82,254 @@
     return ::grpc::Status::OK;
   }
 
-  void OnDeviceBonded(hci::AddressWithType device) {}
+  ::grpc::Status FetchUiEvents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                               ::grpc::ServerWriter<UiMsg>* writer) override {
+    return ui_events_.RunLoop(context, writer);
+  }
 
-  void OnDeviceUnbonded(hci::AddressWithType device) {}
+  ::grpc::Status SendUiCallback(::grpc::ServerContext* context, const UiCallbackMsg* request,
+                                ::google::protobuf::Empty* response) override {
+    hci::Address peer;
+    ASSERT(hci::Address::FromString(request->address().address().address(), peer));
+    hci::AddressType remote_type = static_cast<hci::AddressType>(request->address().type());
 
-  void OnDeviceBondFailed(hci::AddressWithType device) {}
+    switch (request->message_type()) {
+      case UiCallbackType::PASSKEY:
+        security_module_->GetSecurityManager()->OnPasskeyEntry(
+            hci::AddressWithType(peer, remote_type), request->numeric_value());
+        break;
+      case UiCallbackType::YES_NO:
+        security_module_->GetSecurityManager()->OnConfirmYesNo(hci::AddressWithType(peer, remote_type),
+                                                               request->boolean());
+        break;
+      case UiCallbackType::PAIRING_PROMPT:
+        security_module_->GetSecurityManager()->OnPairingPromptAccepted(
+            hci::AddressWithType(peer, remote_type), request->boolean());
+        break;
+      default:
+        LOG_ERROR("Unknown UiCallbackType %d", static_cast<int>(request->message_type()));
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Unknown UiCallbackType");
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchBondEvents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                 ::grpc::ServerWriter<BondMsg>* writer) override {
+    return bond_events_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status SetIoCapability(::grpc::ServerContext* context, const IoCapabilityMessage* request,
+                                 ::google::protobuf::Empty* response) override {
+    security_module_->GetFacadeConfigurationApi()->SetIoCapability(
+        static_cast<hci::IoCapability>(request->capability()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetLeIoCapability(
+      ::grpc::ServerContext* context,
+      const LeIoCapabilityMessage* request,
+      ::google::protobuf::Empty* response) override {
+    security_module_->GetFacadeConfigurationApi()->SetLeIoCapability(
+        static_cast<security::IoCapability>(request->capabilities()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetAuthenticationRequirements(::grpc::ServerContext* context,
+                                               const AuthenticationRequirementsMessage* request,
+                                               ::google::protobuf::Empty* response) override {
+    security_module_->GetFacadeConfigurationApi()->SetAuthenticationRequirements(
+        static_cast<hci::AuthenticationRequirements>(request->requirement()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetOobDataPresent(::grpc::ServerContext* context, const OobDataMessage* request,
+                                   ::google::protobuf::Empty* response) override {
+    security_module_->GetFacadeConfigurationApi()->SetOobData(
+        static_cast<hci::OobDataPresent>(request->data_present()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetLeAuthRequirements(
+      ::grpc::ServerContext* context,
+      const LeAuthRequirementsMessage* request,
+      ::google::protobuf::Empty* response) override {
+    uint8_t auth_req = request->bond() ? AUTH_REQ_BOND : AUTH_REQ_NO_BOND;
+
+    if (request->mitm()) auth_req |= AUTH_REQ_MITM_MASK;
+    if (request->secure_connections()) auth_req |= AUTH_REQ_SECURE_CONNECTIONS_MASK;
+    if (request->keypress()) auth_req |= AUTH_REQ_KEYPRESS_MASK;
+    if (request->ct2()) auth_req |= AUTH_REQ_CT2_MASK;
+    if (request->reserved_bits()) auth_req |= (((request->reserved_bits()) << 6) & AUTH_REQ_RFU_MASK);
+
+    security_module_->GetFacadeConfigurationApi()->SetLeAuthRequirements(auth_req);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetLeInitiatorAddressPolicy(
+      ::grpc::ServerContext* context, const hci::PrivacyPolicy* request, ::google::protobuf::Empty* response) override {
+    Address address = Address::kEmpty;
+    hci::LeAddressManager::AddressPolicy address_policy =
+        static_cast<hci::LeAddressManager::AddressPolicy>(request->address_policy());
+    if (address_policy == hci::LeAddressManager::AddressPolicy::USE_STATIC_ADDRESS) {
+      ASSERT(Address::FromString(request->address_with_type().address().address(), address));
+    }
+    hci::AddressWithType address_with_type(address, static_cast<hci::AddressType>(request->address_with_type().type()));
+    crypto_toolbox::Octet16 irk = {};
+    auto request_irk_length = request->rotation_irk().end() - request->rotation_irk().begin();
+    if (request_irk_length == crypto_toolbox::OCTET16_LEN) {
+      std::vector<uint8_t> irk_data(request->rotation_irk().begin(), request->rotation_irk().end());
+      std::copy_n(irk_data.begin(), crypto_toolbox::OCTET16_LEN, irk.begin());
+    } else {
+      ASSERT(request_irk_length == 0);
+    }
+    auto minimum_rotation_time = std::chrono::milliseconds(request->minimum_rotation_time());
+    auto maximum_rotation_time = std::chrono::milliseconds(request->maximum_rotation_time());
+    security_module_->GetSecurityManager()->SetLeInitiatorAddressPolicy(
+        address_policy, address_with_type, irk, minimum_rotation_time, maximum_rotation_time);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchEnforceSecurityPolicyEvents(
+      ::grpc::ServerContext* context,
+      const ::google::protobuf::Empty* request,
+      ::grpc::ServerWriter<EnforceSecurityPolicyMsg>* writer) override {
+    return enforce_security_policy_events_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status EnforceSecurityPolicy(
+      ::grpc::ServerContext* context,
+      const SecurityPolicyMessage* request,
+      ::google::protobuf::Empty* response) override {
+    hci::Address peer;
+    ASSERT(hci::Address::FromString(request->address().address().address(), peer));
+    hci::AddressType peer_type = static_cast<hci::AddressType>(request->address().type());
+    hci::AddressWithType peer_with_type(peer, peer_type);
+    l2cap::classic::SecurityEnforcementInterface::ResultCallback callback =
+        security_handler_->BindOnceOn(this, &SecurityModuleFacadeService::EnforceSecurityPolicyEvent);
+    security_module_->GetFacadeConfigurationApi()->EnforceSecurityPolicy(
+        peer_with_type, static_cast<l2cap::classic::SecurityPolicy>(request->policy()), std::move(callback));
+    return ::grpc::Status::OK;
+  }
+
+  void DisplayPairingPrompt(const bluetooth::hci::AddressWithType& peer, std::string name) {
+    LOG_INFO("%s", peer.ToString().c_str());
+    UiMsg display_yes_no;
+    display_yes_no.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_yes_no.mutable_peer()->set_type(static_cast<facade::BluetoothAddressTypeEnum>(peer.GetAddressType()));
+    display_yes_no.set_message_type(UiMsgType::DISPLAY_PAIRING_PROMPT);
+    display_yes_no.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_yes_no);
+  }
+
+  virtual void DisplayConfirmValue(const bluetooth::hci::AddressWithType& peer, std::string name,
+                                   uint32_t numeric_value) {
+    LOG_INFO("%s value = 0x%x", peer.ToString().c_str(), numeric_value);
+    UiMsg display_with_value;
+    display_with_value.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_with_value.mutable_peer()->set_type(static_cast<facade::BluetoothAddressTypeEnum>(peer.GetAddressType()));
+    display_with_value.set_message_type(UiMsgType::DISPLAY_YES_NO_WITH_VALUE);
+    display_with_value.set_numeric_value(numeric_value);
+    display_with_value.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_with_value);
+  }
+
+  void DisplayYesNoDialog(const bluetooth::hci::AddressWithType& peer, std::string name) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    UiMsg display_yes_no;
+    display_yes_no.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_yes_no.mutable_peer()->set_type(static_cast<facade::BluetoothAddressTypeEnum>(peer.GetAddressType()));
+    display_yes_no.set_message_type(UiMsgType::DISPLAY_YES_NO);
+    display_yes_no.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_yes_no);
+  }
+
+  void DisplayPasskey(const bluetooth::hci::AddressWithType& peer, std::string name, uint32_t passkey) override {
+    LOG_INFO("%s value = 0x%x", peer.ToString().c_str(), passkey);
+    UiMsg display_passkey;
+    display_passkey.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_passkey.mutable_peer()->set_type(static_cast<facade::BluetoothAddressTypeEnum>(peer.GetAddressType()));
+    display_passkey.set_message_type(UiMsgType::DISPLAY_PASSKEY);
+    display_passkey.set_numeric_value(passkey);
+    display_passkey.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_passkey);
+  }
+
+  void DisplayEnterPasskeyDialog(const bluetooth::hci::AddressWithType& peer, std::string name) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    UiMsg display_passkey_input;
+    display_passkey_input.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_passkey_input.mutable_peer()->set_type(
+        static_cast<facade::BluetoothAddressTypeEnum>(peer.GetAddressType()));
+    display_passkey_input.set_message_type(UiMsgType::DISPLAY_PASSKEY_ENTRY);
+    display_passkey_input.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_passkey_input);
+  }
+
+  void Cancel(const bluetooth::hci::AddressWithType& peer) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    UiMsg display_cancel;
+    display_cancel.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_cancel.mutable_peer()->set_type(static_cast<facade::BluetoothAddressTypeEnum>(peer.GetAddressType()));
+    display_cancel.set_message_type(UiMsgType::DISPLAY_CANCEL);
+    display_cancel.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_cancel);
+  }
+
+  void OnDeviceBonded(hci::AddressWithType peer) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    BondMsg bonded;
+    bonded.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    bonded.mutable_peer()->set_type(static_cast<facade::BluetoothAddressTypeEnum>(peer.GetAddressType()));
+    bonded.set_message_type(BondMsgType::DEVICE_BONDED);
+    bond_events_.OnIncomingEvent(bonded);
+  }
+
+  void OnEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view) override {}
+
+  void OnDeviceUnbonded(hci::AddressWithType peer) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    BondMsg unbonded;
+    unbonded.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    unbonded.mutable_peer()->set_type(static_cast<facade::BluetoothAddressTypeEnum>(peer.GetAddressType()));
+    unbonded.set_message_type(BondMsgType::DEVICE_UNBONDED);
+    bond_events_.OnIncomingEvent(unbonded);
+  }
+
+  void OnDeviceBondFailed(hci::AddressWithType peer) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    BondMsg bond_failed;
+    bond_failed.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    bond_failed.mutable_peer()->set_type(static_cast<facade::BluetoothAddressTypeEnum>(peer.GetAddressType()));
+    bond_failed.set_message_type(BondMsgType::DEVICE_BOND_FAILED);
+    bond_events_.OnIncomingEvent(bond_failed);
+  }
+
+  void EnforceSecurityPolicyEvent(bool result) {
+    EnforceSecurityPolicyMsg msg;
+    msg.set_result(result);
+    enforce_security_policy_events_.OnIncomingEvent(msg);
+  }
 
  private:
-  SecurityModule* security_module_ __attribute__((unused));
-  l2cap::le::L2capLeModule* l2cap_le_module_ __attribute__((unused));
-  l2cap::classic::L2capClassicModule* l2cap_classic_module_ __attribute__((unused));
-  ::bluetooth::os::Handler* security_handler_ __attribute__((unused));
+  SecurityModule* security_module_;
+  ::bluetooth::os::Handler* security_handler_;
+  ::bluetooth::grpc::GrpcEventQueue<UiMsg> ui_events_{"UI events"};
+  ::bluetooth::grpc::GrpcEventQueue<BondMsg> bond_events_{"Bond events"};
+  ::bluetooth::grpc::GrpcEventQueue<EnforceSecurityPolicyMsg> enforce_security_policy_events_{
+      "Enforce Security Policy Events"};
+  uint32_t unique_id{1};
+  std::map<uint32_t, common::OnceCallback<void(bool)>> user_yes_no_callbacks_;
+  std::map<uint32_t, common::OnceCallback<void(uint32_t)>> user_passkey_callbacks_;
 };
 
 void SecurityModuleFacadeModule::ListDependencies(ModuleList* list) {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<SecurityModule>();
-  list->add<l2cap::le::L2capLeModule>();
-  list->add<l2cap::classic::L2capClassicModule>();
-  list->add<hci::HciLayer>();
 }
 
 void SecurityModuleFacadeModule::Start() {
   ::bluetooth::grpc::GrpcFacadeModule::Start();
-  service_ = new SecurityModuleFacadeService(GetDependency<SecurityModule>(), GetDependency<l2cap::le::L2capLeModule>(),
-                                             GetDependency<l2cap::classic::L2capClassicModule>(),
-                                             GetDependency<hci::HciLayer>(), GetHandler());
+  service_ = new SecurityModuleFacadeService(GetDependency<SecurityModule>(), GetHandler());
 }
 
 void SecurityModuleFacadeModule::Stop() {
diff --git a/gd/security/facade.h b/gd/security/facade.h
index fb655d1..8f060c2 100644
--- a/gd/security/facade.h
+++ b/gd/security/facade.h
@@ -18,7 +18,6 @@
 #include <grpc++/grpc++.h>
 
 #include "grpc/grpc_module.h"
-#include "hci/address_with_type.h"
 
 namespace bluetooth {
 namespace security {
diff --git a/gd/security/facade.proto b/gd/security/facade.proto
index 394c35b..772fa9d 100644
--- a/gd/security/facade.proto
+++ b/gd/security/facade.proto
@@ -4,9 +4,128 @@
 
 import "google/protobuf/empty.proto";
 import "facade/common.proto";
+import "l2cap/classic/facade.proto";
+import "hci/facade/le_initiator_address_facade.proto";
 
 service SecurityModuleFacade {
   rpc CreateBond(facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {}
+  rpc CreateBondLe(facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {}
   rpc CancelBond(facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {}
   rpc RemoveBond(facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {}
+  rpc SetIoCapability(IoCapabilityMessage) returns (google.protobuf.Empty) {}
+  rpc SetAuthenticationRequirements(AuthenticationRequirementsMessage) returns (google.protobuf.Empty) {}
+  rpc SetOobDataPresent(OobDataMessage) returns (google.protobuf.Empty) {}
+  rpc SetLeIoCapability(LeIoCapabilityMessage) returns (google.protobuf.Empty) {}
+  rpc SetLeAuthRequirements(LeAuthRequirementsMessage) returns (google.protobuf.Empty) {}
+  rpc SetLeInitiatorAddressPolicy(hci.PrivacyPolicy) returns (google.protobuf.Empty) {}
+  rpc SendUiCallback(UiCallbackMsg) returns (google.protobuf.Empty) {}
+  rpc FetchUiEvents(google.protobuf.Empty) returns (stream UiMsg) {}
+  rpc FetchBondEvents(google.protobuf.Empty) returns (stream BondMsg) {}
+  rpc EnforceSecurityPolicy(SecurityPolicyMessage) returns (google.protobuf.Empty) {}
+  rpc FetchEnforceSecurityPolicyEvents(google.protobuf.Empty) returns (stream EnforceSecurityPolicyMsg) {}
+}
+
+enum UiMsgType {
+  DISPLAY_YES_NO_WITH_VALUE = 0;
+  DISPLAY_YES_NO = 1;
+  DISPLAY_PASSKEY = 2;
+  DISPLAY_PASSKEY_ENTRY = 3;
+  DISPLAY_CANCEL = 4;
+  DISPLAY_PAIRING_PROMPT = 5;
+}
+
+message UiMsg {
+  UiMsgType message_type = 1;
+  facade.BluetoothAddressWithType peer = 2;
+  uint32 numeric_value = 3;
+  uint32 unique_id = 4;
+}
+
+enum UiCallbackType {
+  YES_NO = 0;
+  PASSKEY = 1;
+  PAIRING_PROMPT = 2;
+}
+
+message UiCallbackMsg {
+  UiCallbackType message_type = 1;
+  bool boolean = 2;
+  uint32 numeric_value = 3;
+  uint32 unique_id = 4;
+  facade.BluetoothAddressWithType address = 5;
+}
+
+enum BondMsgType {
+  DEVICE_BONDED = 0;
+  DEVICE_UNBONDED = 1;
+  DEVICE_BOND_FAILED = 2;
+}
+
+message BondMsg {
+  BondMsgType message_type = 1;
+  facade.BluetoothAddressWithType peer = 2;
+}
+
+enum IoCapabilities {
+  DISPLAY_ONLY = 0;
+  DISPLAY_YES_NO_IO_CAP = 1;
+  KEYBOARD_ONLY = 2;
+  NO_INPUT_NO_OUTPUT = 3;
+}
+
+message IoCapabilityMessage {
+  IoCapabilities capability = 1;
+}
+
+message LeIoCapabilityMessage {
+  enum LeIoCapabilities {
+    DISPLAY_ONLY = 0;
+    DISPLAY_YES_NO_IO_CAP = 1;
+    KEYBOARD_ONLY = 2;
+    NO_INPUT_NO_OUTPUT = 3;
+    KEYBOARD_DISPLAY = 4;
+  }
+  LeIoCapabilities capabilities = 1;
+}
+
+enum AuthenticationRequirements {
+  NO_BONDING = 0;
+  NO_BONDING_MITM_PROTECTION = 1;
+  DEDICATED_BONDING = 2;
+  DEDICATED_BONDING_MITM_PROTECTION = 3;
+  GENERAL_BONDING = 4;
+  GENERAL_BONDING_MITM_PROTECTION = 5;
+}
+
+message AuthenticationRequirementsMessage {
+  AuthenticationRequirements requirement = 1;
+}
+
+message LeAuthRequirementsMessage {
+  bool bond = 1;
+  bool mitm = 2;
+  bool secure_connections = 3;
+  bool keypress = 4;
+  bool ct2 = 5;
+  uint32 reserved_bits = 6;
+}
+
+enum OobDataPresent {
+  NOT_PRESENT = 0;
+  P192_PRESENT = 1;
+  P256_PRESENT = 2;
+  P192_AND_256_PRESENT = 3;
+}
+
+message OobDataMessage {
+  OobDataPresent data_present = 1;
+}
+
+message SecurityPolicyMessage {
+  facade.BluetoothAddressWithType address = 1;
+  l2cap.classic.ClassicSecurityPolicy policy = 2;
+}
+
+message EnforceSecurityPolicyMsg {
+  bool result = 1;
 }
diff --git a/gd/security/facade_configuration_api.cc b/gd/security/facade_configuration_api.cc
new file mode 100644
index 0000000..5c4a329
--- /dev/null
+++ b/gd/security/facade_configuration_api.cc
@@ -0,0 +1,63 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#include "facade_configuration_api.h"
+
+#include "common/bind.h"
+#include "l2cap/classic/security_enforcement_interface.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace security {
+
+void FacadeConfigurationApi::SetIoCapability(hci::IoCapability io_capability) {
+  security_handler_->CallOn(security_manager_impl_, &internal::SecurityManagerImpl::SetIoCapability, io_capability);
+}
+
+void FacadeConfigurationApi::SetAuthenticationRequirements(hci::AuthenticationRequirements authentication_requirement) {
+  security_handler_->CallOn(
+      security_manager_impl_,
+      &internal::SecurityManagerImpl::SetAuthenticationRequirements,
+      authentication_requirement);
+}
+
+void FacadeConfigurationApi::SetOobData(hci::OobDataPresent data_present) {
+  security_handler_->CallOn(security_manager_impl_, &internal::SecurityManagerImpl::SetOobDataPresent, data_present);
+}
+
+void FacadeConfigurationApi::SetLeIoCapability(security::IoCapability io_capability) {
+  security_handler_->CallOn(security_manager_impl_, &internal::SecurityManagerImpl::SetLeIoCapability, io_capability);
+}
+
+void FacadeConfigurationApi::SetLeAuthRequirements(uint8_t auth_req) {
+  security_handler_->CallOn(security_manager_impl_, &internal::SecurityManagerImpl::SetLeAuthRequirements, auth_req);
+}
+
+void FacadeConfigurationApi::EnforceSecurityPolicy(
+    hci::AddressWithType remote,
+    l2cap::classic::SecurityPolicy policy,
+    l2cap::classic::SecurityEnforcementInterface::ResultCallback callback) {
+  security_handler_->CallOn(
+      security_manager_impl_,
+      &internal::SecurityManagerImpl::EnforceSecurityPolicy,
+      remote,
+      policy,
+      std::move(callback));
+}
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/facade_configuration_api.h b/gd/security/facade_configuration_api.h
new file mode 100644
index 0000000..bb01372
--- /dev/null
+++ b/gd/security/facade_configuration_api.h
@@ -0,0 +1,63 @@
+/*
+ *
+ *  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.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "hci/address_with_type.h"
+#include "hci/hci_packets.h"
+#include "security/internal/security_manager_impl.h"
+#include "security/smp_packets.h"
+
+namespace bluetooth {
+namespace security {
+
+/**
+ * Manages the security attributes, pairing, bonding of devices, and the
+ * encryption/decryption of communications.
+ */
+class FacadeConfigurationApi {
+ public:
+  friend class internal::SecurityManagerImpl;
+  friend class SecurityModule;
+
+  void SetIoCapability(hci::IoCapability io_capability);
+  void SetAuthenticationRequirements(hci::AuthenticationRequirements authentication_requirement);
+  void SetOobData(hci::OobDataPresent oob_present);
+  void EnforceSecurityPolicy(
+      hci::AddressWithType remote,
+      l2cap::classic::SecurityPolicy policy,
+      l2cap::classic::SecurityEnforcementInterface::ResultCallback callback);
+
+  void SetLeIoCapability(security::IoCapability io_capability);
+  void SetLeAuthRequirements(uint8_t auth_req);
+
+ protected:
+  FacadeConfigurationApi(os::Handler* security_handler, internal::SecurityManagerImpl* security_manager_impl)
+      : security_handler_(security_handler), security_manager_impl_(security_manager_impl) {}
+
+ private:
+  os::Handler* security_handler_ = nullptr;
+  internal::SecurityManagerImpl* security_manager_impl_;
+  DISALLOW_COPY_AND_ASSIGN(FacadeConfigurationApi);
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/initial_informations.h b/gd/security/initial_informations.h
index 4e5efa4..56a6bce 100644
--- a/gd/security/initial_informations.h
+++ b/gd/security/initial_informations.h
@@ -37,11 +37,18 @@
 namespace bluetooth {
 namespace security {
 
-using DistributedKeys =
-    std::tuple<std::optional<crypto_toolbox::Octet16> /* ltk */, std::optional<uint16_t> /*ediv*/,
-               std::optional<std::array<uint8_t, 8>> /* rand */, std::optional<Address> /* Identity address */,
-               AddrType, std::optional<crypto_toolbox::Octet16> /* IRK */,
-               std::optional<crypto_toolbox::Octet16>> /* Signature Key */;
+struct DistributedKeys {
+  /* LE Keys*/
+  std::optional<crypto_toolbox::Octet16> ltk;
+  std::optional<uint16_t> ediv;
+  std::optional<std::array<uint8_t, 8>> rand;
+  std::optional<hci::AddressWithType> identity_address;
+  std::optional<crypto_toolbox::Octet16> irk;
+  std::optional<crypto_toolbox::Octet16> signature_key;
+
+  /* BR/EDR Keys */
+  std::optional<crypto_toolbox::Octet16> link_key;
+};
 
 /* This class represents the result of pairing, as returned from Pairing Handler */
 struct PairingResult {
@@ -98,7 +105,8 @@
   std::optional<MyOobData> my_oob_data;
 
   /* Used by Pairing Handler to present user with requests*/
-  UI* ui_handler;
+  UI* user_interface;
+  os::Handler* user_interface_handler;
 
   /* HCI interface to use */
   hci::LeSecurityInterface* le_security_interface;
diff --git a/gd/security/internal/security_manager_impl.cc b/gd/security/internal/security_manager_impl.cc
index 51759c8..c3fdfe2 100644
--- a/gd/security/internal/security_manager_impl.cc
+++ b/gd/security/internal/security_manager_impl.cc
@@ -19,58 +19,50 @@
 
 #include <iostream>
 
+#include "common/bind.h"
+#include "crypto_toolbox/crypto_toolbox.h"
 #include "hci/address_with_type.h"
 #include "os/log.h"
 #include "security/initial_informations.h"
+#include "security/internal/security_manager_impl.h"
+#include "security/pairing_handler_le.h"
 #include "security/security_manager.h"
+#include "security/ui.h"
 
 namespace bluetooth {
 namespace security {
 namespace internal {
 
-std::shared_ptr<bluetooth::security::record::SecurityRecord> SecurityManagerImpl::CreateSecurityRecord(
-    hci::Address address) {
-  hci::AddressWithType device(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS);
-  // Security record check
-  auto entry = security_record_map_.find(device.GetAddress());
-  if (entry == security_record_map_.end()) {
-    LOG_INFO("No security record for device: %s ", device.ToString().c_str());
-    // Create one
-    std::shared_ptr<security::record::SecurityRecord> record =
-        std::make_shared<security::record::SecurityRecord>(device);
-    auto new_entry = std::pair<hci::Address, std::shared_ptr<security::record::SecurityRecord>>(
-        record->GetDevice().GetAddress(), record);
-    // Keep track of it
-    security_record_map_.insert(new_entry);
-    return record;
-  }
-  return entry->second;
-}
-
-void SecurityManagerImpl::DispatchPairingHandler(std::shared_ptr<security::record::SecurityRecord> record,
-                                                 bool locally_initiated) {
+void SecurityManagerImpl::DispatchPairingHandler(
+    std::shared_ptr<record::SecurityRecord> record, bool locally_initiated) {
   common::OnceCallback<void(hci::Address, PairingResultOrFailure)> callback =
       common::BindOnce(&SecurityManagerImpl::OnPairingHandlerComplete, common::Unretained(this));
-  auto entry = pairing_handler_map_.find(record->GetDevice().GetAddress());
+  auto entry = pairing_handler_map_.find(record->GetPseudoAddress().GetAddress());
   if (entry != pairing_handler_map_.end()) {
     LOG_WARN("Device already has a pairing handler, and is in the middle of pairing!");
     return;
   }
   std::shared_ptr<pairing::PairingHandler> pairing_handler = nullptr;
-  switch (record->GetDevice().GetAddressType()) {
-    case hci::AddressType::PUBLIC_DEVICE_ADDRESS:
+  switch (record->GetPseudoAddress().GetAddressType()) {
+    case hci::AddressType::PUBLIC_DEVICE_ADDRESS: {
       pairing_handler = std::make_shared<security::pairing::ClassicPairingHandler>(
-          l2cap_classic_module_->GetFixedChannelManager(), security_manager_channel_, record, security_handler_,
-          std::move(callback));
+          security_manager_channel_,
+          record,
+          security_handler_,
+          std::move(callback),
+          user_interface_,
+          user_interface_handler_,
+          "TODO: grab device name properly");
       break;
+    }
     default:
-      ASSERT_LOG(false, "Pairing type %hhu not implemented!", record->GetDevice().GetAddressType());
+      ASSERT_LOG(false, "Pairing type %hhu not implemented!", record->GetPseudoAddress().GetAddressType());
   }
-  auto new_entry = std::pair<hci::Address, std::shared_ptr<pairing::PairingHandler>>(record->GetDevice().GetAddress(),
-                                                                                     pairing_handler);
+  auto new_entry = std::pair<hci::Address, std::shared_ptr<pairing::PairingHandler>>(
+      record->GetPseudoAddress().GetAddress(), pairing_handler);
   pairing_handler_map_.insert(std::move(new_entry));
-  pairing_handler->Initiate(locally_initiated, pairing::kDefaultIoCapability, pairing::kDefaultOobDataPresent,
-                            pairing::kDefaultAuthenticationRequirements);
+  pairing_handler->Initiate(locally_initiated, this->local_io_capability_, this->local_oob_data_present_,
+                            this->local_authentication_requirements_);
 }
 
 void SecurityManagerImpl::Init() {
@@ -81,7 +73,7 @@
 }
 
 void SecurityManagerImpl::CreateBond(hci::AddressWithType device) {
-  auto record = CreateSecurityRecord(device.GetAddress());
+  auto record = security_database_.FindOrCreate(device);
   if (record->IsBonded()) {
     NotifyDeviceBonded(device);
   } else {
@@ -90,6 +82,20 @@
   }
 }
 
+void SecurityManagerImpl::CreateBondLe(hci::AddressWithType address) {
+  auto record = security_database_.FindOrCreate(address);
+  if (record->IsBonded()) {
+    NotifyDeviceBondFailed(address, PairingFailure("Already bonded"));
+    return;
+  }
+
+  pending_le_pairing_.address_ = address;
+
+  l2cap_manager_le_->ConnectServices(
+      address, common::BindOnce(&SecurityManagerImpl::OnConnectionFailureLe, common::Unretained(this)),
+      security_handler_);
+}
+
 void SecurityManagerImpl::CancelBond(hci::AddressWithType device) {
   auto entry = pairing_handler_map_.find(device.GetAddress());
   if (entry != pairing_handler_map_.end()) {
@@ -101,15 +107,31 @@
 
 void SecurityManagerImpl::RemoveBond(hci::AddressWithType device) {
   CancelBond(device);
-  auto entry = security_record_map_.find(device.GetAddress());
-  if (entry != security_record_map_.end()) {
-    security_record_map_.erase(entry);
-  }
+  security_database_.Remove(device);
+  security_manager_channel_->Disconnect(device.GetAddress());
   // Signal disconnect
   // Remove security record
   // Signal Remove from database
 }
 
+void SecurityManagerImpl::SetUserInterfaceHandler(UI* user_interface, os::Handler* handler) {
+  if (user_interface_ != nullptr || user_interface_handler_ != nullptr) {
+    LOG_ALWAYS_FATAL("Listener has already been registered!");
+  }
+  user_interface_ = user_interface;
+  user_interface_handler_ = handler;
+}
+
+void SecurityManagerImpl::SetLeInitiatorAddressPolicy(
+    hci::LeAddressManager::AddressPolicy address_policy,
+    hci::AddressWithType fixed_address,
+    crypto_toolbox::Octet16 rotation_irk,
+    std::chrono::milliseconds minimum_rotation_time,
+    std::chrono::milliseconds maximum_rotation_time) {
+  acl_manager_->SetPrivacyPolicyForInitiatorAddress(
+      address_policy, fixed_address, rotation_irk, minimum_rotation_time, maximum_rotation_time);
+}
+
 void SecurityManagerImpl::RegisterCallbackListener(ISecurityManagerListener* listener, os::Handler* handler) {
   for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
     if (it->first == listener) {
@@ -149,31 +171,37 @@
     iter.second->Post(
         common::Bind(&ISecurityManagerListener::OnDeviceUnbonded, common::Unretained(iter.first), device));
   }
+  acl_manager_->RemoveDeviceFromConnectList(device);
+}
+
+void SecurityManagerImpl::NotifyEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view) {
+  for (auto& iter : listeners_) {
+    iter.second->Post(common::Bind(&ISecurityManagerListener::OnEncryptionStateChanged, common::Unretained(iter.first),
+                                   encryption_change_view));
+  }
 }
 
 template <class T>
 void SecurityManagerImpl::HandleEvent(T packet) {
   ASSERT(packet.IsValid());
   auto entry = pairing_handler_map_.find(packet.GetBdAddr());
-  if (entry != pairing_handler_map_.end()) {
-    entry->second->OnReceive(packet);
-  } else {
+
+  if (entry == pairing_handler_map_.end()) {
     auto bd_addr = packet.GetBdAddr();
     auto event_code = packet.GetEventCode();
-    auto event = hci::EventPacketView::Create(std::move(packet));
-    ASSERT_LOG(event.IsValid(), "Received invalid packet");
-    const hci::EventCode code = event.GetEventCode();
-    auto record = CreateSecurityRecord(bd_addr);
-    switch (code) {
-      case hci::EventCode::LINK_KEY_REQUEST:
-        DispatchPairingHandler(record, true);
-        break;
-      default:
-        LOG_ERROR("No classic pairing handler for device '%s' ready for command '%hhx' ", bd_addr.ToString().c_str(),
-                  event_code);
-        break;
+
+    if (event_code != hci::EventCode::LINK_KEY_REQUEST) {
+      LOG_ERROR("No classic pairing handler for device '%s' ready for command %s ", bd_addr.ToString().c_str(),
+                hci::EventCodeText(event_code).c_str());
+      return;
     }
+
+    auto record =
+        security_database_.FindOrCreate(hci::AddressWithType{bd_addr, hci::AddressType::PUBLIC_DEVICE_ADDRESS});
+    DispatchPairingHandler(record, true);
+    entry = pairing_handler_map_.find(bd_addr);
   }
+  entry->second->OnReceive(packet);
 }
 
 void SecurityManagerImpl::OnHciEventReceived(hci::EventPacketView packet) {
@@ -214,21 +242,92 @@
     case hci::EventCode::USER_PASSKEY_REQUEST:
       HandleEvent(hci::UserPasskeyRequestView::Create(event));
       break;
+    case hci::EventCode::REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION:
+      LOG_INFO("Unhandled event: %s", hci::EventCodeText(code).c_str());
+      break;
+
+    case hci::EventCode::ENCRYPTION_CHANGE: {
+      EncryptionChangeView encryption_change_view = EncryptionChangeView::Create(event);
+      if (!encryption_change_view.IsValid()) {
+        LOG_ERROR("Invalid EncryptionChange packet received");
+        return;
+      }
+      if (encryption_change_view.GetConnectionHandle() == pending_le_pairing_.connection_handle_) {
+        pending_le_pairing_.handler_->OnHciEvent(event);
+        return;
+      }
+      NotifyEncryptionStateChanged(encryption_change_view);
+      break;
+    }
+
     default:
       ASSERT_LOG(false, "Cannot handle received packet: %s", hci::EventCodeText(code).c_str());
       break;
   }
 }
 
+void SecurityManagerImpl::OnConnectionClosed(hci::Address address) {
+  auto entry = pairing_handler_map_.find(address);
+  if (entry != pairing_handler_map_.end()) {
+    LOG_DEBUG("Cancelling pairing handler for '%s'", address.ToString().c_str());
+    entry->second->Cancel();
+  }
+}
+
+void SecurityManagerImpl::OnHciLeEvent(hci::LeMetaEventView event) {
+  // hci::SubeventCode::LONG_TERM_KEY_REQUEST,
+  // hci::SubeventCode::READ_LOCAL_P256_PUBLIC_KEY_COMPLETE,
+  // hci::SubeventCode::GENERATE_DHKEY_COMPLETE,
+  LOG_ERROR("Unhandled HCI LE security event");
+}
+
+void SecurityManagerImpl::OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  auto entry = pairing_handler_map_.find(address.GetAddress());
+  if (entry != pairing_handler_map_.end()) {
+    entry->second->OnPairingPromptAccepted(address, confirmed);
+  } else {
+    pending_le_pairing_.handler_->OnUiAction(PairingEvent::UI_ACTION_TYPE::PAIRING_ACCEPTED, confirmed);
+  }
+}
+
+void SecurityManagerImpl::OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  auto entry = pairing_handler_map_.find(address.GetAddress());
+  if (entry != pairing_handler_map_.end()) {
+    entry->second->OnConfirmYesNo(address, confirmed);
+  } else {
+    if (pending_le_pairing_.address_ == address) {
+      pending_le_pairing_.handler_->OnUiAction(PairingEvent::UI_ACTION_TYPE::CONFIRM_YESNO, confirmed);
+    }
+  }
+}
+
+void SecurityManagerImpl::OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) {
+  auto entry = pairing_handler_map_.find(address.GetAddress());
+  if (entry != pairing_handler_map_.end()) {
+    entry->second->OnPasskeyEntry(address, passkey);
+  } else {
+    if (pending_le_pairing_.address_ == address) {
+      pending_le_pairing_.handler_->OnUiAction(PairingEvent::UI_ACTION_TYPE::PASSKEY, passkey);
+    }
+  }
+}
+
 void SecurityManagerImpl::OnPairingHandlerComplete(hci::Address address, PairingResultOrFailure status) {
   auto entry = pairing_handler_map_.find(address);
   if (entry != pairing_handler_map_.end()) {
     pairing_handler_map_.erase(entry);
+    security_manager_channel_->Release(address);
+  }
+  auto remote = hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS);
+  auto cb_entry = enforce_security_policy_callback_map_.find(remote);
+  if (cb_entry != enforce_security_policy_callback_map_.end()) {
+    this->InternalEnforceSecurityPolicy(remote, cb_entry->second.first, std::move(cb_entry->second.second), false);
+    enforce_security_policy_callback_map_.erase(cb_entry);
   }
   if (!std::holds_alternative<PairingFailure>(status)) {
-    NotifyDeviceBonded(hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS));
+    NotifyDeviceBonded(remote);
   } else {
-    NotifyDeviceBondFailed(hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS), status);
+    NotifyDeviceBondFailed(remote, status);
   }
 }
 
@@ -238,24 +337,309 @@
   ASSERT_LOG(result == bluetooth::l2cap::le::FixedChannelManager::RegistrationResult::SUCCESS,
              "Failed to register to LE SMP Fixed Channel Service");
 }
-void SecurityManagerImpl::OnConnectionOpenLe(std::unique_ptr<l2cap::le::FixedChannel> channel) {}
-void SecurityManagerImpl::OnConnectionClosedLe(hci::AddressWithType address, hci::ErrorCode error_code) {}
-void SecurityManagerImpl::OnConnectionFailureLe(bluetooth::l2cap::le::FixedChannelManager::ConnectionResult result) {}
-void SecurityManagerImpl::OnHciLeEvent(hci::LeMetaEventView event) {}
-SecurityManagerImpl::SecurityManagerImpl(os::Handler* security_handler, l2cap::le::L2capLeModule* l2cap_le_module,
-                                         l2cap::classic::L2capClassicModule* l2cap_classic_module,
-                                         channel::SecurityManagerChannel* security_manager_channel,
-                                         hci::HciLayer* hci_layer)
-    : security_handler_(security_handler), l2cap_le_module_(l2cap_le_module),
-      l2cap_classic_module_(l2cap_classic_module), l2cap_manager_le_(l2cap_le_module_->GetFixedChannelManager()),
-      hci_security_interface_le_(hci_layer->GetLeSecurityInterface(
-          common::Bind(&SecurityManagerImpl::OnHciLeEvent, common::Unretained(this)), security_handler)),
-      security_manager_channel_(security_manager_channel) {
+
+LeFixedChannelEntry* SecurityManagerImpl::FindStoredLeChannel(const hci::AddressWithType& device) {
+  for (LeFixedChannelEntry& storage : all_channels_) {
+    if (storage.channel_->GetDevice() == device) {
+      return &storage;
+    }
+  }
+  return nullptr;
+}
+
+bool SecurityManagerImpl::EraseStoredLeChannel(const hci::AddressWithType& device) {
+  for (auto it = all_channels_.begin(); it != all_channels_.end(); it++) {
+    if (it->channel_->GetDevice() == device) {
+      all_channels_.erase(it);
+      return true;
+    }
+  }
+  return false;
+}
+
+void SecurityManagerImpl::OnSmpCommandLe(hci::AddressWithType device) {
+  LeFixedChannelEntry* stored_chan = FindStoredLeChannel(device);
+  if (!stored_chan) {
+    LOG_ALWAYS_FATAL("Received SMP command for unknown channel");
+    return;
+  }
+
+  std::unique_ptr<l2cap::le::FixedChannel>& channel = stored_chan->channel_;
+
+  auto packet = channel->GetQueueUpEnd()->TryDequeue();
+  if (!packet) {
+    LOG_ERROR("Received dequeue, but no data ready...");
+    return;
+  }
+
+  // Pending pairing - pass the data to the handler
+  auto temp_cmd_view = CommandView::Create(*packet);
+  if (pending_le_pairing_.address_ == device) {
+    pending_le_pairing_.handler_->OnCommandView(temp_cmd_view);
+    return;
+  }
+
+  // no pending pairing attempt
+  if (!temp_cmd_view.IsValid()) {
+    LOG_ERROR("Invalid Command packet");
+    return;
+  }
+
+  if (temp_cmd_view.GetCode() == Code::SECURITY_REQUEST) {
+    // TODO: either start encryption or pairing
+    LOG_WARN("Unhandled security request!!!");
+    return;
+  }
+
+  auto my_role = channel->GetLinkOptions()->GetRole();
+  if (temp_cmd_view.GetCode() == Code::PAIRING_REQUEST && my_role == hci::Role::SLAVE) {
+    // TODO: if (pending_le_pairing_) { do not start another }
+
+    LOG_INFO("start of security request handling!");
+
+    PairingRequestView pairing_request = PairingRequestView::Create(temp_cmd_view);
+    auto& enqueue_buffer = stored_chan->enqueue_buffer_;
+
+    // TODO: this doesn't have to be a unique ptr, if there is a way to properly std::move it into place where it's
+    // stored
+    pending_le_pairing_.connection_handle_ = channel->GetLinkOptions()->GetHandle();
+    InitialInformations initial_informations{
+        .my_role = my_role,
+        .my_connection_address = channel->GetLinkOptions()->GetLocalAddress(),
+        /*TODO: properly obtain capabilities from device-specific storage*/
+        .myPairingCapabilities = {.io_capability = local_le_io_capability_,
+                                  .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                                  .auth_req = local_le_auth_req_,
+                                  .maximum_encryption_key_size = 16,
+                                  .initiator_key_distribution = 0x07,
+                                  .responder_key_distribution = 0x07},
+        .remotely_initiated = true,
+        .connection_handle = channel->GetLinkOptions()->GetHandle(),
+        .remote_connection_address = channel->GetDevice(),
+        .remote_name = "TODO: grab proper device name in sec mgr",
+        /* contains pairing request, if the pairing was remotely initiated */
+        .pairing_request = pairing_request,
+        .remote_oob_data = std::nullopt,  // TODO:
+        .my_oob_data = std::nullopt,      // TODO:
+        /* Used by Pairing Handler to present user with requests*/
+        .user_interface = user_interface_,
+        .user_interface_handler = user_interface_handler_,
+
+        /* HCI interface to use */
+        .le_security_interface = hci_security_interface_le_,
+        .proper_l2cap_interface = enqueue_buffer.get(),
+        .l2cap_handler = security_handler_,
+        /* Callback to execute once the Pairing process is finished */
+        // TODO: make it an common::OnceCallback ?
+        .OnPairingFinished = std::bind(&SecurityManagerImpl::OnPairingFinished, this, std::placeholders::_1),
+    };
+    pending_le_pairing_.address_ = device;
+    pending_le_pairing_.handler_ = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
+  }
+}
+
+void SecurityManagerImpl::OnConnectionOpenLe(std::unique_ptr<l2cap::le::FixedChannel> channel_param) {
+  auto enqueue_buffer_temp =
+      std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(channel_param->GetQueueUpEnd());
+
+  all_channels_.push_back({std::move(channel_param), std::move(enqueue_buffer_temp)});
+  auto& stored_channel = all_channels_.back();
+  auto& channel = stored_channel.channel_;
+  auto& enqueue_buffer = stored_channel.enqueue_buffer_;
+
+  channel->RegisterOnCloseCallback(
+      security_handler_,
+      common::BindOnce(&SecurityManagerImpl::OnConnectionClosedLe, common::Unretained(this), channel->GetDevice()));
+  channel->GetQueueUpEnd()->RegisterDequeue(
+      security_handler_,
+      common::Bind(&SecurityManagerImpl::OnSmpCommandLe, common::Unretained(this), channel->GetDevice()));
+
+  if (pending_le_pairing_.address_ != channel->GetDevice()) {
+    return;
+  }
+
+  // TODO: this doesn't have to be a unique ptr, if there is a way to properly std::move it into place where it's stored
+  pending_le_pairing_.connection_handle_ = channel->GetLinkOptions()->GetHandle();
+  InitialInformations initial_informations{
+      .my_role = channel->GetLinkOptions()->GetRole(),
+      .my_connection_address = channel->GetLinkOptions()->GetLocalAddress(),
+      /*TODO: properly obtain capabilities from device-specific storage*/
+      .myPairingCapabilities = {.io_capability = local_le_io_capability_,
+                                .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                                .auth_req = local_le_auth_req_,
+                                .maximum_encryption_key_size = 16,
+                                .initiator_key_distribution = 0x07,
+                                .responder_key_distribution = 0x07},
+      .remotely_initiated = false,
+      .connection_handle = channel->GetLinkOptions()->GetHandle(),
+      .remote_connection_address = channel->GetDevice(),
+      .remote_name = "TODO: grab proper device name in sec mgr",
+      /* contains pairing request, if the pairing was remotely initiated */
+      .pairing_request = std::nullopt,  // TODO: handle remotely initiated pairing in SecurityManager properly
+      .remote_oob_data = std::nullopt,  // TODO:
+      .my_oob_data = std::nullopt,      // TODO:
+      /* Used by Pairing Handler to present user with requests*/
+      .user_interface = user_interface_,
+      .user_interface_handler = user_interface_handler_,
+
+      /* HCI interface to use */
+      .le_security_interface = hci_security_interface_le_,
+      .proper_l2cap_interface = enqueue_buffer.get(),
+      .l2cap_handler = security_handler_,
+      /* Callback to execute once the Pairing process is finished */
+      // TODO: make it an common::OnceCallback ?
+      .OnPairingFinished = std::bind(&SecurityManagerImpl::OnPairingFinished, this, std::placeholders::_1),
+  };
+  pending_le_pairing_.handler_ = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
+}
+
+void SecurityManagerImpl::OnConnectionClosedLe(hci::AddressWithType address, hci::ErrorCode error_code) {
+  if (pending_le_pairing_.address_ != address) {
+    LeFixedChannelEntry* stored_chan = FindStoredLeChannel(address);
+    if (!stored_chan) {
+      LOG_ALWAYS_FATAL("Received connection closed for unknown channel");
+      return;
+    }
+    stored_chan->channel_->GetQueueUpEnd()->UnregisterDequeue();
+    stored_chan->enqueue_buffer_.reset();
+    EraseStoredLeChannel(address);
+    return;
+  }
+  pending_le_pairing_.handler_->SendExitSignal();
+  NotifyDeviceBondFailed(address, PairingFailure("Connection closed"));
+}
+
+void SecurityManagerImpl::OnConnectionFailureLe(bluetooth::l2cap::le::FixedChannelManager::ConnectionResult result) {
+  if (result.connection_result_code ==
+      bluetooth::l2cap::le::FixedChannelManager::ConnectionResultCode::FAIL_ALL_SERVICES_HAVE_CHANNEL) {
+    // TODO: already connected
+  }
+
+  // This callback is invoked only for devices we attempted to connect to.
+  NotifyDeviceBondFailed(pending_le_pairing_.address_, PairingFailure("Connection establishment failed"));
+}
+
+SecurityManagerImpl::SecurityManagerImpl(
+    os::Handler* security_handler,
+    l2cap::le::L2capLeModule* l2cap_le_module,
+    channel::SecurityManagerChannel* security_manager_channel,
+    hci::HciLayer* hci_layer,
+    hci::AclManager* acl_manager)
+    : security_handler_(security_handler),
+      l2cap_le_module_(l2cap_le_module),
+      l2cap_manager_le_(l2cap_le_module_->GetFixedChannelManager()),
+      hci_security_interface_le_(
+          hci_layer->GetLeSecurityInterface(security_handler_->BindOn(this, &SecurityManagerImpl::OnHciLeEvent))),
+      security_manager_channel_(security_manager_channel),
+      acl_manager_(acl_manager) {
+  Init();
+
   l2cap_manager_le_->RegisterService(
-      bluetooth::l2cap::kSmpCid, {},
+      bluetooth::l2cap::kSmpCid,
       common::BindOnce(&SecurityManagerImpl::OnL2capRegistrationCompleteLe, common::Unretained(this)),
       common::Bind(&SecurityManagerImpl::OnConnectionOpenLe, common::Unretained(this)), security_handler_);
 }
+
+void SecurityManagerImpl::OnPairingFinished(security::PairingResultOrFailure pairing_result) {
+  LOG_INFO(" â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â–  Received pairing result");
+
+  if (std::holds_alternative<PairingFailure>(pairing_result)) {
+    PairingFailure failure = std::get<PairingFailure>(pairing_result);
+    LOG_INFO(" â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â–  failure message: %s",
+             failure.message.c_str());
+    return;
+  }
+
+  auto result = std::get<PairingResult>(pairing_result);
+  LOG_INFO("Pairing with %s was successful", result.connection_address.ToString().c_str());
+  NotifyDeviceBonded(result.connection_address);
+}
+
+// Facade Configuration API functions
+void SecurityManagerImpl::SetIoCapability(hci::IoCapability io_capability) {
+  this->local_io_capability_ = io_capability;
+}
+
+void SecurityManagerImpl::SetLeIoCapability(security::IoCapability io_capability) {
+  this->local_le_io_capability_ = io_capability;
+}
+
+void SecurityManagerImpl::SetLeAuthRequirements(uint8_t auth_req) {
+  this->local_le_auth_req_ = auth_req;
+}
+
+void SecurityManagerImpl::SetAuthenticationRequirements(hci::AuthenticationRequirements authentication_requirements) {
+  this->local_authentication_requirements_ = authentication_requirements;
+}
+
+void SecurityManagerImpl::SetOobDataPresent(hci::OobDataPresent data_present) {
+  this->local_oob_data_present_ = data_present;
+}
+
+void SecurityManagerImpl::InternalEnforceSecurityPolicy(
+    hci::AddressWithType remote,
+    l2cap::classic::SecurityPolicy policy,
+    l2cap::classic::SecurityEnforcementInterface::ResultCallback result_callback,
+    bool try_meet_requirements) {
+  bool result = false;
+  auto record = this->security_database_.FindOrCreate(remote);
+  switch (policy) {
+    case l2cap::classic::SecurityPolicy::BEST:
+    case l2cap::classic::SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT:
+      result = record->IsAuthenticated() && record->RequiresMitmProtection() && record->IsEncryptionRequired();
+      break;
+    case l2cap::classic::SecurityPolicy::ENCRYPTED_TRANSPORT:
+      result = record->IsAuthenticated() && record->IsEncryptionRequired();
+      break;
+    case l2cap::classic::SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK:
+      result = true;
+      break;
+  }
+  if (!result && try_meet_requirements) {
+    auto entry = enforce_security_policy_callback_map_.find(remote);
+    if (entry != enforce_security_policy_callback_map_.end()) {
+      LOG_WARN("Callback already pending for remote: '%s' !", remote.ToString().c_str());
+    } else {
+      enforce_security_policy_callback_map_.emplace(
+          remote,
+          std::pair<l2cap::classic::SecurityPolicy, l2cap::classic::SecurityEnforcementInterface::ResultCallback>(
+              policy, std::move(result_callback)));
+      DispatchPairingHandler(record, true);
+    }
+    return;
+  }
+  result_callback.Invoke(result);
+}
+
+void SecurityManagerImpl::EnforceSecurityPolicy(
+    hci::AddressWithType remote,
+    l2cap::classic::SecurityPolicy policy,
+    l2cap::classic::SecurityEnforcementInterface::ResultCallback result_callback) {
+  this->InternalEnforceSecurityPolicy(remote, policy, std::move(result_callback), true);
+}
+
+void SecurityManagerImpl::EnforceLeSecurityPolicy(
+    hci::AddressWithType remote, l2cap::le::SecurityPolicy policy,
+    l2cap::le::SecurityEnforcementInterface::ResultCallback result_callback) {
+  bool result = false;
+  // TODO(jpawlowski): Implement for LE
+  switch (policy) {
+    case l2cap::le::SecurityPolicy::BEST:
+      break;
+    case l2cap::le::SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT:
+      break;
+    case l2cap::le::SecurityPolicy::ENCRYPTED_TRANSPORT:
+      break;
+    case l2cap::le::SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK:
+      result = true;
+      break;
+    case l2cap::le::SecurityPolicy::_NOT_FOR_YOU__AUTHENTICATED_PAIRING_WITH_128_BIT_KEY:
+      break;
+    case l2cap::le::SecurityPolicy::_NOT_FOR_YOU__AUTHORIZATION:
+      break;
+  }
+  result_callback.Invoke(result);
+}
 }  // namespace internal
 }  // namespace security
 }  // namespace bluetooth
diff --git a/gd/security/internal/security_manager_impl.h b/gd/security/internal/security_manager_impl.h
index bb022a2..646be1c 100644
--- a/gd/security/internal/security_manager_impl.h
+++ b/gd/security/internal/security_manager_impl.h
@@ -19,27 +19,53 @@
 #include <unordered_map>
 #include <utility>
 
+#include "hci/acl_manager.h"
 #include "hci/classic_device.h"
-#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/classic/security_enforcement_interface.h"
 #include "l2cap/le/l2cap_le_module.h"
+#include "l2cap/le/security_enforcement_interface.h"
 #include "os/handler.h"
 #include "security/channel/security_manager_channel.h"
+#include "security/initial_informations.h"
 #include "security/pairing/classic_pairing_handler.h"
+#include "security/pairing_handler_le.h"
 #include "security/record/security_record.h"
+#include "security/record/security_record_database.h"
 
 namespace bluetooth {
 namespace security {
 
 class ISecurityManagerListener;
 
+static constexpr hci::IoCapability kDefaultIoCapability = hci::IoCapability::DISPLAY_YES_NO;
+static constexpr hci::OobDataPresent kDefaultOobDataPresent = hci::OobDataPresent::NOT_PRESENT;
+static constexpr hci::AuthenticationRequirements kDefaultAuthenticationRequirements =
+    hci::AuthenticationRequirements::GENERAL_BONDING;
+
 namespace internal {
 
-class SecurityManagerImpl : public channel::ISecurityManagerChannelListener {
+struct LeFixedChannelEntry {
+  std::unique_ptr<l2cap::le::FixedChannel> channel_;
+  std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> enqueue_buffer_;
+};
+
+class SecurityManagerImpl : public channel::ISecurityManagerChannelListener, public UICallbacks {
  public:
-  explicit SecurityManagerImpl(os::Handler* security_handler, l2cap::le::L2capLeModule* l2cap_le_module,
-                               l2cap::classic::L2capClassicModule* l2cap_classic_module,
-                               channel::SecurityManagerChannel* security_manager_channel, hci::HciLayer* hci_layer);
-  virtual ~SecurityManagerImpl() = default;
+  explicit SecurityManagerImpl(
+      os::Handler* security_handler,
+      l2cap::le::L2capLeModule* l2cap_le_module,
+      channel::SecurityManagerChannel* security_manager_channel,
+      hci::HciLayer* hci_layer,
+      hci::AclManager* acl_manager);
+
+  ~SecurityManagerImpl() {
+    /* L2CAP layer doesn't guarantee to send the registered OnCloseCallback during shutdown. Cleanup the remaining
+     * queues to prevent crashes */
+    for (auto& stored_chan : all_channels_) {
+      stored_chan.channel_->GetQueueUpEnd()->UnregisterDequeue();
+      stored_chan.enqueue_buffer_.reset();
+    }
+  }
 
   // All APIs must be invoked in SM layer handler
 
@@ -49,12 +75,18 @@
   void Init();
 
   /**
-   * Checks the device for existing bond, if not bonded, initiates pairing.
+   * Initiates bond over Classic transport with device, if not bonded yet.
    *
-   * @param device pointer to device we want to bond with
-   * @return true if bonded or pairing started successfully, false if currently pairing
+   * @param address device address we want to bond with
    */
-  void CreateBond(hci::AddressWithType device);
+  void CreateBond(hci::AddressWithType address);
+
+  /**
+   * Initiates bond over Low Energy transport with device, if not bonded yet.
+   *
+   * @param address device address we want to bond with
+   */
+  void CreateBondLe(hci::AddressWithType address);
 
   /* void CreateBond(std::shared_ptr<hci::LeDevice> device); */
 
@@ -62,7 +94,6 @@
    * Cancels the pairing process for this device.
    *
    * @param device pointer to device with which we want to cancel our bond
-   * @return <code>true</code> if successfully stopped
    */
   void CancelBond(hci::AddressWithType device);
 
@@ -79,6 +110,21 @@
   /* void RemoveBond(std::shared_ptr<hci::LeDevice> device); */
 
   /**
+   * Register Security UI handler, for handling prompts around the Pairing process.
+   */
+  void SetUserInterfaceHandler(UI* user_interface, os::Handler* handler);
+
+  /**
+   * Specify the initiator address policy used for LE transport. Can only be called once.
+   */
+  void SetLeInitiatorAddressPolicy(
+      hci::LeAddressManager::AddressPolicy address_policy,
+      hci::AddressWithType fixed_address,
+      crypto_toolbox::Octet16 rotation_irk,
+      std::chrono::milliseconds minimum_rotation_time,
+      std::chrono::milliseconds maximum_rotation_time);
+
+  /**
    * Register to listen for callback events from SecurityManager
    *
    * @param listener ISecurityManagerListener instance to handle callbacks
@@ -100,6 +146,13 @@
   void OnHciEventReceived(hci::EventPacketView packet) override;
 
   /**
+   * When a conncetion closes we should clean up the pairing handler
+   *
+   * @param address Remote address
+   */
+  void OnConnectionClosed(hci::Address address) override;
+
+  /**
    * Pairing handler has finished or cancelled
    *
    * @param address address for pairing handler
@@ -107,33 +160,78 @@
    */
   void OnPairingHandlerComplete(hci::Address address, PairingResultOrFailure status);
 
+  // UICallbacks implementation
+  void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) override;
+
+  // Facade Configuration API functions
+  void SetIoCapability(hci::IoCapability io_capability);
+  void SetAuthenticationRequirements(hci::AuthenticationRequirements authentication_requirements);
+  void SetOobDataPresent(hci::OobDataPresent data_present);
+  void SetLeIoCapability(security::IoCapability io_capability);
+  void SetLeAuthRequirements(uint8_t auth_req);
+
+  void EnforceSecurityPolicy(hci::AddressWithType remote, l2cap::classic::SecurityPolicy policy,
+                             l2cap::classic::SecurityEnforcementInterface::ResultCallback result_callback);
+  void EnforceLeSecurityPolicy(hci::AddressWithType remote, l2cap::le::SecurityPolicy policy,
+                               l2cap::le::SecurityEnforcementInterface::ResultCallback result_callback);
  protected:
   std::vector<std::pair<ISecurityManagerListener*, os::Handler*>> listeners_;
+  UI* user_interface_ = nullptr;
+  os::Handler* user_interface_handler_ = nullptr;
+
   void NotifyDeviceBonded(hci::AddressWithType device);
   void NotifyDeviceBondFailed(hci::AddressWithType device, PairingResultOrFailure status);
   void NotifyDeviceUnbonded(hci::AddressWithType device);
+  void NotifyEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view);
 
  private:
   template <class T>
   void HandleEvent(T packet);
 
-  std::shared_ptr<record::SecurityRecord> CreateSecurityRecord(hci::Address address);
   void DispatchPairingHandler(std::shared_ptr<record::SecurityRecord> record, bool locally_initiated);
   void OnL2capRegistrationCompleteLe(l2cap::le::FixedChannelManager::RegistrationResult result,
                                      std::unique_ptr<l2cap::le::FixedChannelService> le_smp_service);
+  void OnSmpCommandLe(hci::AddressWithType device);
   void OnConnectionOpenLe(std::unique_ptr<l2cap::le::FixedChannel> channel);
   void OnConnectionClosedLe(hci::AddressWithType address, hci::ErrorCode error_code);
   void OnConnectionFailureLe(bluetooth::l2cap::le::FixedChannelManager::ConnectionResult result);
+  void OnPairingFinished(bluetooth::security::PairingResultOrFailure pairing_result);
   void OnHciLeEvent(hci::LeMetaEventView event);
+  LeFixedChannelEntry* FindStoredLeChannel(const hci::AddressWithType& device);
+  bool EraseStoredLeChannel(const hci::AddressWithType& device);
+  void InternalEnforceSecurityPolicy(
+      hci::AddressWithType remote,
+      l2cap::classic::SecurityPolicy policy,
+      l2cap::classic::SecurityEnforcementInterface::ResultCallback result_callback,
+      bool try_meet_requirements);
 
   os::Handler* security_handler_ __attribute__((unused));
   l2cap::le::L2capLeModule* l2cap_le_module_ __attribute__((unused));
-  l2cap::classic::L2capClassicModule* l2cap_classic_module_ __attribute__((unused));
   std::unique_ptr<l2cap::le::FixedChannelManager> l2cap_manager_le_;
   hci::LeSecurityInterface* hci_security_interface_le_ __attribute__((unused));
-  channel::SecurityManagerChannel* security_manager_channel_ __attribute__((unused));
-  std::unordered_map<hci::Address, std::shared_ptr<record::SecurityRecord>> security_record_map_;
+  channel::SecurityManagerChannel* security_manager_channel_;
+  hci::AclManager* acl_manager_;
+  record::SecurityRecordDatabase security_database_;
   std::unordered_map<hci::Address, std::shared_ptr<pairing::PairingHandler>> pairing_handler_map_;
+  hci::IoCapability local_io_capability_ = kDefaultIoCapability;
+  hci::AuthenticationRequirements local_authentication_requirements_ = kDefaultAuthenticationRequirements;
+  hci::OobDataPresent local_oob_data_present_ = kDefaultOobDataPresent;
+  security::IoCapability local_le_io_capability_ = security::IoCapability::NO_INPUT_NO_OUTPUT;
+  uint8_t local_le_auth_req_ = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc;
+  std::unordered_map<
+      hci::AddressWithType,
+      std::pair<l2cap::classic::SecurityPolicy, l2cap::classic::SecurityEnforcementInterface::ResultCallback>>
+      enforce_security_policy_callback_map_;
+
+  struct {
+    hci::AddressWithType address_;
+    uint16_t connection_handle_;
+    std::unique_ptr<PairingHandlerLe> handler_;
+  } pending_le_pairing_;
+
+  std::list<LeFixedChannelEntry> all_channels_;
 };
 }  // namespace internal
 }  // namespace security
diff --git a/gd/security/l2cap_security_module_interface.cc b/gd/security/l2cap_security_module_interface.cc
new file mode 100644
index 0000000..939d06b
--- /dev/null
+++ b/gd/security/l2cap_security_module_interface.cc
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+#include "security/l2cap_security_module_interface.h"
+#include "common/bind.h"
+
+namespace bluetooth {
+namespace security {
+
+L2capSecurityModuleInterface::L2capSecurityModuleInterface(internal::SecurityManagerImpl* security_manager_impl,
+                                                           os::Handler* security_handler)
+    : security_manager_impl_(security_manager_impl), security_handler_(security_handler) {}
+
+void L2capSecurityModuleInterface::Enforce(
+    hci::AddressWithType remote, l2cap::classic::SecurityPolicy policy,
+    l2cap::classic::SecurityEnforcementInterface::ResultCallback result_callback) {
+  this->security_handler_->Post(common::BindOnce(
+      &internal::SecurityManagerImpl::EnforceSecurityPolicy, common::Unretained(security_manager_impl_),
+      std::forward<hci::AddressWithType>(remote), std::forward<l2cap::classic::SecurityPolicy>(policy),
+      std::forward<l2cap::classic::SecurityEnforcementInterface::ResultCallback>(result_callback)));
+}
+
+void L2capSecurityModuleInterface::Enforce(hci::AddressWithType remote, l2cap::le::SecurityPolicy policy,
+                                           l2cap::le::SecurityEnforcementInterface::ResultCallback result_callback) {
+  this->security_handler_->Post(common::BindOnce(
+      &internal::SecurityManagerImpl::EnforceLeSecurityPolicy, common::Unretained(security_manager_impl_),
+      std::forward<hci::AddressWithType>(remote), std::forward<l2cap::le::SecurityPolicy>(policy),
+      std::forward<l2cap::le::SecurityEnforcementInterface::ResultCallback>(result_callback)));
+}
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/l2cap_security_module_interface.h b/gd/security/l2cap_security_module_interface.h
new file mode 100644
index 0000000..9a8fc8e
--- /dev/null
+++ b/gd/security/l2cap_security_module_interface.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include "l2cap/classic/security_enforcement_interface.h"
+#include "l2cap/le/security_enforcement_interface.h"
+#include "os/handler.h"
+#include "security/internal/security_manager_impl.h"
+
+namespace bluetooth {
+namespace security {
+class L2capSecurityModuleInterface : public l2cap::classic::SecurityEnforcementInterface,
+                                     public l2cap::le::SecurityEnforcementInterface {
+ public:
+  L2capSecurityModuleInterface(internal::SecurityManagerImpl* security_manager_impl, os::Handler* security_handler);
+  void Enforce(hci::AddressWithType remote, l2cap::classic::SecurityPolicy policy,
+               l2cap::classic::SecurityEnforcementInterface::ResultCallback result_callback) override;
+  void Enforce(hci::AddressWithType remote, l2cap::le::SecurityPolicy policy,
+               l2cap::le::SecurityEnforcementInterface::ResultCallback result_callback) override;
+
+ private:
+  internal::SecurityManagerImpl* security_manager_impl_;
+  os::Handler* security_handler_ = nullptr;
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/pairing/classic_pairing_handler.cc b/gd/security/pairing/classic_pairing_handler.cc
index bf9a332..46b0118 100644
--- a/gd/security/pairing/classic_pairing_handler.cc
+++ b/gd/security/pairing/classic_pairing_handler.cc
@@ -17,44 +17,64 @@
  */
 #include "security/pairing/classic_pairing_handler.h"
 
+#include "common/bind.h"
+
 namespace bluetooth {
 namespace security {
 namespace pairing {
 
-void ClassicPairingHandler::OnRegistrationComplete(
-    l2cap::classic::FixedChannelManager::RegistrationResult result,
-    std::unique_ptr<l2cap::classic::FixedChannelService> fixed_channel_service) {
-  fixed_channel_service_ = std::move(fixed_channel_service);
-  fixed_channel_manager_->ConnectServices(
-      GetRecord()->GetDevice().GetAddress(),
-      common::Bind(&ClassicPairingHandler::OnConnectionFail, common::Unretained(this)), security_handler_);
+void ClassicPairingHandler::NotifyUiDisplayYesNo(uint32_t numeric_value) {
+  ASSERT(user_interface_handler_ != nullptr);
+  user_interface_handler_->Post(common::BindOnce(&UI::DisplayConfirmValue, common::Unretained(user_interface_),
+                                                 GetRecord()->GetPseudoAddress(), device_name_, numeric_value));
 }
 
-void ClassicPairingHandler::OnUnregistered() {
-  std::move(complete_callback_).Run(GetRecord()->GetDevice().GetAddress(), last_status_);
+void ClassicPairingHandler::NotifyUiDisplayYesNo() {
+  ASSERT(user_interface_handler_ != nullptr);
+  user_interface_handler_->Post(common::BindOnce(&UI::DisplayYesNoDialog, common::Unretained(user_interface_),
+                                                 GetRecord()->GetPseudoAddress(), device_name_));
 }
 
-void ClassicPairingHandler::OnConnectionOpen(std::unique_ptr<l2cap::classic::FixedChannel> fixed_channel) {
-  ASSERT(fixed_channel_ == nullptr);
-  fixed_channel_ = std::move(fixed_channel);
-  fixed_channel_->Acquire();
-  fixed_channel_->RegisterOnCloseCallback(
-      security_handler_, common::BindOnce(&ClassicPairingHandler::OnConnectionClose, common::Unretained(this)));
+void ClassicPairingHandler::NotifyUiDisplayPasskey(uint32_t passkey) {
+  ASSERT(user_interface_handler_ != nullptr);
+  user_interface_handler_->Post(common::BindOnce(&UI::DisplayPasskey, common::Unretained(user_interface_),
+                                                 GetRecord()->GetPseudoAddress(), device_name_, passkey));
 }
 
-void ClassicPairingHandler::OnConnectionFail(l2cap::classic::FixedChannelManager::ConnectionResult result) {
-  Cancel();
+void ClassicPairingHandler::NotifyUiDisplayPasskeyInput() {
+  ASSERT(user_interface_handler_ != nullptr);
+  user_interface_handler_->Post(common::BindOnce(&UI::DisplayEnterPasskeyDialog, common::Unretained(user_interface_),
+                                                 GetRecord()->GetPseudoAddress(), device_name_));
 }
-void ClassicPairingHandler::OnConnectionClose(hci::ErrorCode error_code) {
-  // Called when the connection gets closed
-  LOG_ERROR("Connection closed due to: %s", hci::ErrorCodeText(error_code).c_str());
-  ASSERT(fixed_channel_ != nullptr);
-  Cancel();
+
+void ClassicPairingHandler::NotifyUiDisplayCancel() {
+  ASSERT(user_interface_handler_ != nullptr);
+  user_interface_handler_->Post(
+      common::BindOnce(&UI::Cancel, common::Unretained(user_interface_), GetRecord()->GetPseudoAddress()));
+}
+
+void ClassicPairingHandler::OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  LOG_WARN("TODO Not Implemented!");
+}
+
+void ClassicPairingHandler::OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  if (confirmed) {
+    GetChannel()->SendCommand(
+        hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+  } else {
+    GetChannel()->SendCommand(
+        hci::UserConfirmationRequestNegativeReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+  }
+}
+
+void ClassicPairingHandler::OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) {
+  LOG_WARN("TODO Not Implemented!");
 }
 
 void ClassicPairingHandler::Initiate(bool locally_initiated, hci::IoCapability io_capability,
                                      hci::OobDataPresent oob_present,
                                      hci::AuthenticationRequirements auth_requirements) {
+  LOG_DEBUG("Initiate");
   locally_initiated_ = locally_initiated;
   local_io_capability_ = io_capability;
   local_oob_present_ = oob_present;
@@ -62,21 +82,17 @@
 
   // TODO(optedoblivion): Read OOB data
   // if host and controller support secure connections used HCIREADLOCALOOBEXTENDEDDATA vs HCIREADLOCALOOBDATA
-
-  fixed_channel_manager_->RegisterService(
-      l2cap::kClassicPairingTriggerCid, security_policy_,
-      common::Bind(&ClassicPairingHandler::OnRegistrationComplete, common::Unretained(this)),
-      common::Bind(&ClassicPairingHandler::OnConnectionOpen, common::Unretained(this)), security_handler_);
+  GetChannel()->Connect(GetRecord()->GetPseudoAddress().GetAddress());
 }
 
 void ClassicPairingHandler::Cancel() {
-  if (fixed_channel_ != nullptr) {
-    fixed_channel_->Release();
+  if (is_cancelled_) return;
+  is_cancelled_ = true;
+  PairingResultOrFailure result = PairingResult();
+  if (last_status_ != hci::ErrorCode::SUCCESS) {
+    result = PairingFailure(hci::ErrorCodeText(last_status_));
   }
-  if (fixed_channel_service_ != nullptr) {
-    fixed_channel_service_->Unregister(common::Bind(&ClassicPairingHandler::OnUnregistered, common::Unretained(this)),
-                                       security_handler_);
-  }
+  std::move(complete_callback_).Run(GetRecord()->GetPseudoAddress().GetAddress(), result);
 }
 
 void ClassicPairingHandler::OnReceive(hci::ChangeConnectionLinkKeyCompleteView packet) {
@@ -92,20 +108,20 @@
 void ClassicPairingHandler::OnReceive(hci::PinCodeRequestView packet) {
   ASSERT(packet.IsValid());
   LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
 }
 
 void ClassicPairingHandler::OnReceive(hci::LinkKeyRequestView packet) {
   ASSERT(packet.IsValid());
   // TODO(optedoblivion): Add collision detection here
   LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
   if (GetRecord()->IsBonded() || GetRecord()->IsPaired()) {
-    auto packet =
-        hci::LinkKeyRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress(), GetRecord()->GetLinkKey());
+    auto packet = hci::LinkKeyRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress(),
+                                                          GetRecord()->GetLinkKey());
     this->GetChannel()->SendCommand(std::move(packet));
   } else {
-    auto packet = hci::LinkKeyRequestNegativeReplyBuilder::Create(GetRecord()->GetDevice().GetAddress());
+    auto packet = hci::LinkKeyRequestNegativeReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress());
     this->GetChannel()->SendCommand(std::move(packet));
   }
 }
@@ -113,37 +129,73 @@
 void ClassicPairingHandler::OnReceive(hci::LinkKeyNotificationView packet) {
   ASSERT(packet.IsValid());
   LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
   GetRecord()->SetLinkKey(packet.GetLinkKey(), packet.GetKeyType());
+  Cancel();
 }
 
 void ClassicPairingHandler::OnReceive(hci::IoCapabilityRequestView packet) {
   ASSERT(packet.IsValid());
   LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
   hci::IoCapability io_capability = local_io_capability_;
   hci::OobDataPresent oob_present = hci::OobDataPresent::NOT_PRESENT;
   hci::AuthenticationRequirements authentication_requirements = local_authentication_requirements_;
-  auto reply_packet = hci::IoCapabilityRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress(), io_capability,
-                                                                   oob_present, authentication_requirements);
+  auto reply_packet = hci::IoCapabilityRequestReplyBuilder::Create(
+      GetRecord()->GetPseudoAddress().GetAddress(), io_capability, oob_present, authentication_requirements);
   this->GetChannel()->SendCommand(std::move(reply_packet));
 }
 
 void ClassicPairingHandler::OnReceive(hci::IoCapabilityResponseView packet) {
   ASSERT(packet.IsValid());
   LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
 
   // Using local variable until device database pointer is ready
   remote_io_capability_ = packet.GetIoCapability();
-  // TODO(optedoblivion): device->SetIoCapability(packet.GetIoCapability);
+  remote_authentication_requirements_ = packet.GetAuthenticationRequirements();
+  remote_oob_present_ = packet.GetOobDataPresent();
+  switch (remote_authentication_requirements_) {
+    case hci::AuthenticationRequirements::NO_BONDING:
+      GetRecord()->SetIsEncryptionRequired(false);
+      GetRecord()->SetRequiresMitmProtection(false);
+      break;
+    case hci::AuthenticationRequirements::NO_BONDING_MITM_PROTECTION:
+      GetRecord()->SetIsEncryptionRequired(false);
+      GetRecord()->SetRequiresMitmProtection(true);
+      break;
+    case hci::AuthenticationRequirements::DEDICATED_BONDING:
+      GetRecord()->SetIsEncryptionRequired(true);
+      GetRecord()->SetRequiresMitmProtection(false);
+      break;
+    case hci::AuthenticationRequirements::DEDICATED_BONDING_MITM_PROTECTION:
+      GetRecord()->SetIsEncryptionRequired(true);
+      GetRecord()->SetRequiresMitmProtection(true);
+      break;
+    case hci::AuthenticationRequirements::GENERAL_BONDING:
+      GetRecord()->SetIsEncryptionRequired(true);
+      GetRecord()->SetRequiresMitmProtection(false);
+      break;
+    case hci::AuthenticationRequirements::GENERAL_BONDING_MITM_PROTECTION:
+      GetRecord()->SetIsEncryptionRequired(true);
+      GetRecord()->SetRequiresMitmProtection(true);
+      break;
+    default:
+      GetRecord()->SetRequiresMitmProtection(false);
+      break;
+  }
 }
 
 void ClassicPairingHandler::OnReceive(hci::SimplePairingCompleteView packet) {
   ASSERT(packet.IsValid());
   LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
-  Cancel();
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  last_status_ = packet.GetStatus();
+  if (last_status_ != hci::ErrorCode::SUCCESS) {
+    LOG_INFO("Failed SimplePairingComplete: %s", hci::ErrorCodeText(last_status_).c_str());
+    // Cancel here since we won't get LinkKeyNotification
+    Cancel();
+  }
 }
 
 void ClassicPairingHandler::OnReceive(hci::ReturnLinkKeysView packet) {
@@ -164,13 +216,13 @@
 void ClassicPairingHandler::OnReceive(hci::RemoteOobDataRequestView packet) {
   ASSERT(packet.IsValid());
   LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
 }
 
 void ClassicPairingHandler::OnReceive(hci::UserPasskeyNotificationView packet) {
   ASSERT(packet.IsValid());
   LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
 }
 
 void ClassicPairingHandler::OnReceive(hci::KeypressNotificationView packet) {
@@ -204,7 +256,7 @@
 void ClassicPairingHandler::OnReceive(hci::UserConfirmationRequestView packet) {
   ASSERT(packet.IsValid());
   LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
   // if locally_initialized, use default, otherwise us remote io caps
   hci::IoCapability initiator_io_capability = (locally_initiated_) ? local_io_capability_ : remote_io_capability_;
   hci::IoCapability responder_io_capability = (!locally_initiated_) ? local_io_capability_ : remote_io_capability_;
@@ -216,28 +268,32 @@
           // NumericComparison, Both auto confirm
           LOG_INFO("Numeric Comparison: A and B auto confirm");
           GetChannel()->SendCommand(
-              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
         case hci::IoCapability::DISPLAY_YES_NO:
           // NumericComparison, Initiator auto confirm, Responder display
           GetChannel()->SendCommand(
-              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
           LOG_INFO("Numeric Comparison: A auto confirm");
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
         case hci::IoCapability::KEYBOARD_ONLY:
           // PassKey Entry, Initiator display, Responder input
-          // TODO(optedoblivion): Notify UI
-          LOG_INFO("Notify UI");
+          NotifyUiDisplayPasskey(packet.GetNumericValue());
+          LOG_INFO("Passkey Entry: A display, B input");
           // Authenticated
+          GetRecord()->SetAuthenticated(true);
           break;
         case hci::IoCapability::NO_INPUT_NO_OUTPUT:
           // NumericComparison, Both auto confirm
           LOG_INFO("Numeric Comparison: A and B auto confirm");
           GetChannel()->SendCommand(
-              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
       }
       break;
@@ -245,27 +301,31 @@
       switch (responder_io_capability) {
         case hci::IoCapability::DISPLAY_ONLY:
           // NumericComparison, Initiator display, Responder auto confirm
-          // TODO(optedoblivion): Notify UI
-          LOG_INFO("Notify UI");
+          LOG_INFO("Numeric Comparison: A DisplayYesNo, B auto confirm");
+          NotifyUiDisplayYesNo(packet.GetNumericValue());
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
         case hci::IoCapability::DISPLAY_YES_NO:
           // NumericComparison Both Display, Both confirm
-          // TODO(optedoblivion): Notify UI
-          LOG_INFO("Notify UI");
+          LOG_INFO("Numeric Comparison: A and B DisplayYesNo");
+          NotifyUiDisplayYesNo(packet.GetNumericValue());
           // Authenticated
+          GetRecord()->SetAuthenticated(true);
           break;
         case hci::IoCapability::KEYBOARD_ONLY:
           // PassKey Entry, Initiator display, Responder input
-          // TODO(optedoblivion): Notify UI
-          LOG_INFO("Notify UI");
+          NotifyUiDisplayPasskey(packet.GetNumericValue());
+          LOG_INFO("Passkey Entry: A display, B input");
           // Authenticated
+          GetRecord()->SetAuthenticated(true);
           break;
         case hci::IoCapability::NO_INPUT_NO_OUTPUT:
           // NumericComparison, auto confirm Responder, Yes/No confirm Initiator. Don't show confirmation value
-          // TODO(optedoblivion): Notify UI
-          LOG_INFO("Notify UI");
+          NotifyUiDisplayYesNo();
+          LOG_INFO("Numeric Comparison: A DisplayYesNo, B auto confirm, no show value");
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
       }
       break;
@@ -273,28 +333,32 @@
       switch (responder_io_capability) {
         case hci::IoCapability::DISPLAY_ONLY:
           // PassKey Entry, Responder display, Initiator input
-          // TODO(optedoblivion): Notify UI
-          LOG_INFO("Notify UI");
+          NotifyUiDisplayPasskeyInput();
+          LOG_INFO("Passkey Entry: A input, B display");
           // Authenticated
+          GetRecord()->SetAuthenticated(true);
           break;
         case hci::IoCapability::DISPLAY_YES_NO:
           // PassKey Entry, Responder display, Initiator input
-          // TODO(optedoblivion): Notify UI
-          LOG_INFO("Notify UI");
+          NotifyUiDisplayPasskeyInput();
+          LOG_INFO("Passkey Entry: A input, B display");
           // Authenticated
+          GetRecord()->SetAuthenticated(true);
           break;
         case hci::IoCapability::KEYBOARD_ONLY:
           // PassKey Entry, both input
-          // TODO(optedoblivion): Notify UI
-          LOG_INFO("Notify UI");
+          NotifyUiDisplayPasskeyInput();
+          LOG_INFO("Passkey Entry: A input, B input");
           // Authenticated
+          GetRecord()->SetAuthenticated(true);
           break;
         case hci::IoCapability::NO_INPUT_NO_OUTPUT:
           // NumericComparison, both auto confirm
           LOG_INFO("Numeric Comparison: A and B auto confirm");
           GetChannel()->SendCommand(
-              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
       }
       break;
@@ -304,29 +368,33 @@
           // NumericComparison, both auto confirm
           LOG_INFO("Numeric Comparison: A and B auto confirm");
           GetChannel()->SendCommand(
-              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
         case hci::IoCapability::DISPLAY_YES_NO:
           // NumericComparison, Initiator auto confirm, Responder Yes/No confirm, no show conf val
           LOG_INFO("Numeric Comparison: A auto confirm");
           GetChannel()->SendCommand(
-              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
         case hci::IoCapability::KEYBOARD_ONLY:
           // NumericComparison, both auto confirm
           LOG_INFO("Numeric Comparison: A and B auto confirm");
           GetChannel()->SendCommand(
-              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
         case hci::IoCapability::NO_INPUT_NO_OUTPUT:
           // NumericComparison, both auto confirm
           LOG_INFO("Numeric Comparison: A and B auto confirm");
           GetChannel()->SendCommand(
-              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
           // Unauthenticated
+          GetRecord()->SetAuthenticated(false);
           break;
       }
       break;
@@ -335,9 +403,31 @@
 
 void ClassicPairingHandler::OnReceive(hci::UserPasskeyRequestView packet) {
   ASSERT(packet.IsValid());
-  ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+}
+
+void ClassicPairingHandler::OnUserInput(bool user_input) {
+  if (user_input) {
+    UserClickedYes();
+  } else {
+    UserClickedNo();
+  }
+}
+
+void ClassicPairingHandler::UserClickedYes() {
+  GetChannel()->SendCommand(
+      hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+}
+
+void ClassicPairingHandler::UserClickedNo() {
+  GetChannel()->SendCommand(
+      hci::UserConfirmationRequestNegativeReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+}
+
+void ClassicPairingHandler::OnPasskeyInput(uint32_t passkey) {
+  passkey_ = passkey;
 }
 
 }  // namespace pairing
 }  // namespace security
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/security/pairing/classic_pairing_handler.h b/gd/security/pairing/classic_pairing_handler.h
index d4111ea..902e941 100644
--- a/gd/security/pairing/classic_pairing_handler.h
+++ b/gd/security/pairing/classic_pairing_handler.h
@@ -21,30 +21,31 @@
 
 #include <utility>
 
+#include "common/callback.h"
 #include "l2cap/classic/l2cap_classic_module.h"
 #include "security/initial_informations.h"
+#include "security/security_manager_listener.h"
 
 namespace bluetooth {
 namespace security {
-namespace pairing {
 
-static constexpr hci::IoCapability kDefaultIoCapability = hci::IoCapability::DISPLAY_YES_NO;
-static constexpr hci::OobDataPresent kDefaultOobDataPresent = hci::OobDataPresent::NOT_PRESENT;
-static constexpr hci::AuthenticationRequirements kDefaultAuthenticationRequirements =
-    hci::AuthenticationRequirements::DEDICATED_BONDING_MITM_PROTECTION;
+class ISecurityManagerListener;
+
+namespace pairing {
 
 class ClassicPairingHandler : public PairingHandler {
  public:
-  ClassicPairingHandler(std::shared_ptr<l2cap::classic::FixedChannelManager> fixed_channel_manager,
-                        channel::SecurityManagerChannel* security_manager_channel,
+  ClassicPairingHandler(channel::SecurityManagerChannel* security_manager_channel,
                         std::shared_ptr<record::SecurityRecord> record, os::Handler* security_handler,
-                        common::OnceCallback<void(hci::Address, PairingResultOrFailure)> complete_callback)
-      : PairingHandler(security_manager_channel, std::move(record)),
-        fixed_channel_manager_(std::move(fixed_channel_manager)), security_policy_(),
-        security_handler_(security_handler), remote_io_capability_(kDefaultIoCapability),
-        local_io_capability_(kDefaultIoCapability), local_oob_present_(kDefaultOobDataPresent),
-        local_authentication_requirements_(kDefaultAuthenticationRequirements),
-        complete_callback_(std::move(complete_callback)) {}
+                        common::OnceCallback<void(hci::Address, PairingResultOrFailure)> complete_callback,
+                        UI* user_interface, os::Handler* user_interface_handler, std::string device_name)
+      : PairingHandler(security_manager_channel, std::move(record)), security_handler_(security_handler),
+        remote_io_capability_(hci::IoCapability::DISPLAY_YES_NO), remote_oob_present_(hci::OobDataPresent::NOT_PRESENT),
+        remote_authentication_requirements_(hci::AuthenticationRequirements::DEDICATED_BONDING_MITM_PROTECTION),
+        local_io_capability_(hci::IoCapability::DISPLAY_YES_NO), local_oob_present_(hci::OobDataPresent::NOT_PRESENT),
+        local_authentication_requirements_(hci::AuthenticationRequirements::DEDICATED_BONDING_MITM_PROTECTION),
+        complete_callback_(std::move(complete_callback)), user_interface_(user_interface),
+        user_interface_handler_(user_interface_handler), device_name_(std::move(device_name)) {}
 
   ~ClassicPairingHandler() override = default;
 
@@ -69,26 +70,37 @@
   void OnReceive(hci::UserConfirmationRequestView packet) override;
   void OnReceive(hci::UserPasskeyRequestView packet) override;
 
- private:
-  void OnRegistrationComplete(l2cap::classic::FixedChannelManager::RegistrationResult result,
-                              std::unique_ptr<l2cap::classic::FixedChannelService> fixed_channel_service);
-  void OnUnregistered();
-  void OnConnectionOpen(std::unique_ptr<l2cap::classic::FixedChannel> fixed_channel);
-  void OnConnectionFail(l2cap::classic::FixedChannelManager::ConnectionResult result);
-  void OnConnectionClose(hci::ErrorCode error_code);
+  void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) override;
 
-  std::shared_ptr<l2cap::classic::FixedChannelManager> fixed_channel_manager_;
-  std::unique_ptr<l2cap::classic::FixedChannelService> fixed_channel_service_{nullptr};
-  l2cap::SecurityPolicy security_policy_ __attribute__((unused));
+ private:
+  void OnUserInput(bool user_input);
+  void OnPasskeyInput(uint32_t passkey);
+  void NotifyUiDisplayYesNo(uint32_t numeric_value);
+  void NotifyUiDisplayYesNo();
+  void NotifyUiDisplayPasskey(uint32_t passkey);
+  void NotifyUiDisplayPasskeyInput();
+  void NotifyUiDisplayCancel();
+  void UserClickedYes();
+  void UserClickedNo();
+
   os::Handler* security_handler_ __attribute__((unused));
-  hci::IoCapability remote_io_capability_ __attribute__((unused));
-  hci::IoCapability local_io_capability_ __attribute__((unused));
+  hci::IoCapability remote_io_capability_;
+  hci::OobDataPresent remote_oob_present_ __attribute__((unused));
+  hci::AuthenticationRequirements remote_authentication_requirements_ __attribute__((unused));
+  hci::IoCapability local_io_capability_;
   hci::OobDataPresent local_oob_present_ __attribute__((unused));
   hci::AuthenticationRequirements local_authentication_requirements_ __attribute__((unused));
-  std::unique_ptr<l2cap::classic::FixedChannel> fixed_channel_{nullptr};
   common::OnceCallback<void(hci::Address, PairingResultOrFailure)> complete_callback_;
-  PairingResultOrFailure last_status_;
+  UI* user_interface_;
+  os::Handler* user_interface_handler_;
+  std::string device_name_;
+  bool is_cancelled_ = false;
+
+  hci::ErrorCode last_status_ = hci::ErrorCode::UNKNOWN_HCI_COMMAND;
   bool locally_initiated_ = false;
+  uint32_t passkey_ = 0;
 };
 
 }  // namespace pairing
diff --git a/gd/security/pairing/classic_pairing_handler_unittest.cc b/gd/security/pairing/classic_pairing_handler_unittest.cc
index c797522..a7c08fd 100644
--- a/gd/security/pairing/classic_pairing_handler_unittest.cc
+++ b/gd/security/pairing/classic_pairing_handler_unittest.cc
@@ -19,14 +19,15 @@
 
 #include <gtest/gtest.h>
 #include <memory>
+#include <utility>
 
 #include "hci/hci_packets.h"
-#include "l2cap/classic/fixed_channel_manager_mock.h"
 #include "packet/raw_builder.h"
 #include "security/channel/security_manager_channel.h"
 #include "security/initial_informations.h"
 #include "security/smp_packets.h"
 #include "security/test/fake_hci_layer.h"
+#include "security/test/fake_security_interface.h"
 
 namespace bluetooth {
 namespace security {
@@ -45,6 +46,21 @@
 using os::Thread;
 using packet::RawBuilder;
 
+class FakeSecurityManagerChannel : public channel::SecurityManagerChannel {
+ public:
+  FakeSecurityManagerChannel(os::Handler* handler, hci::HciLayer* hci_layer)
+      : channel::SecurityManagerChannel(handler, hci_layer) {}
+  ~FakeSecurityManagerChannel() {}
+
+  void OnLinkConnected(std::unique_ptr<l2cap::classic::LinkSecurityInterface> link) override {
+    LOG_ERROR("CALLED");
+  }
+
+  void OnLinkDisconnected(hci::Address address) override {
+    LOG_ERROR("CALLED");
+  }
+};
+
 class SecurityManagerChannelCallback : public ISecurityManagerChannelListener {
  public:
   explicit SecurityManagerChannelCallback(pairing::ClassicPairingHandler* pairing_handler)
@@ -96,48 +112,80 @@
     }
   }
 
+  void OnConnectionClosed(hci::Address address) override {
+    LOG_DEBUG("Called");
+  }
+
  private:
   pairing::ClassicPairingHandler* pairing_handler_ = nullptr;
 };
 
 static void pairing_complete_callback(bluetooth::hci::Address address, PairingResultOrFailure status) {
-  //  if (std::holds_alternative<PairingResult>(status)) {
-  //    auto result = status::get<PairingResult>(status);
-  //  }
-  //  if (std::holds_alternative<PairingFailure>(status)) {
-  //    auto failure = status::get<PairingFailure>(status);
-  //  }
+  ASSERT_TRUE(std::holds_alternative<PairingResult>(status));
 }
 
 class ClassicPairingHandlerTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    handler_ = new Handler(&thread_);
     hci_layer_ = new FakeHciLayer();
-    channel_ = new channel::SecurityManagerChannel(handler_, hci_layer_);
     fake_registry_.InjectTestModule(&FakeHciLayer::Factory, hci_layer_);
-    fake_registry_.Start<FakeHciLayer>(&thread_);
+    handler_ = fake_registry_.GetTestModuleHandler(&FakeHciLayer::Factory);
+    channel_ = new FakeSecurityManagerChannel(handler_, hci_layer_);
     security_record_ = std::make_shared<record::SecurityRecord>(device_);
-    std::shared_ptr<l2cap::classic::testing::MockFixedChannelManager> sptr =
-        std::shared_ptr<l2cap::classic::testing::MockFixedChannelManager>(
-            new l2cap::classic::testing::MockFixedChannelManager);
-    EXPECT_CALL(*sptr, RegisterService(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_))
-        .Times(::testing::AnyNumber());
-    pairing_handler_ = new pairing::ClassicPairingHandler(sptr, channel_, security_record_, handler_,
-                                                          common::Bind(&pairing_complete_callback));
+    pairing_handler_ = new pairing::ClassicPairingHandler(channel_, security_record_, handler_,
+                                                          common::Bind(&pairing_complete_callback), user_interface_,
+                                                          user_interface_handler_, "Fake name");
     channel_callback_ = new SecurityManagerChannelCallback(pairing_handler_);
     channel_->SetChannelListener(channel_callback_);
+    security_interface_ = new FakeSecurityInterface(handler_, channel_);
+    channel_->SetSecurityInterface(security_interface_);
   }
 
   void TearDown() override {
     channel_->SetChannelListener(nullptr);
-    handler_->Clear();
-    fake_registry_.SynchronizeModuleHandler(&FakeHciLayer::Factory, std::chrono::milliseconds(20));
+    synchronize();
     fake_registry_.StopAll();
     delete pairing_handler_;
-    delete handler_;
     delete channel_;
     delete channel_callback_;
+    delete security_interface_;
+  }
+
+  void synchronize() {
+    fake_registry_.SynchronizeModuleHandler(&FakeHciLayer::Factory, std::chrono::milliseconds(20));
+  }
+
+  void ReceiveLinkKeyRequest(hci::AddressWithType device) {
+    hci_layer_->IncomingEvent(hci::LinkKeyRequestBuilder::Create(device.GetAddress()));
+    synchronize();
+  }
+
+  void ReceiveIoCapabilityRequest(hci::AddressWithType device) {
+    hci_layer_->IncomingEvent(hci::IoCapabilityRequestBuilder::Create(device.GetAddress()));
+    synchronize();
+  }
+
+  void ReceiveIoCapabilityResponse(hci::AddressWithType device, hci::IoCapability io_cap,
+                                   hci::OobDataPresent oob_present, hci::AuthenticationRequirements auth_reqs) {
+    hci_layer_->IncomingEvent(
+        hci::IoCapabilityResponseBuilder::Create(device.GetAddress(), io_cap, oob_present, auth_reqs));
+    synchronize();
+  }
+
+  void ReceiveUserConfirmationRequest(hci::AddressWithType device, uint32_t numeric_value) {
+    hci_layer_->IncomingEvent(hci::UserConfirmationRequestBuilder::Create(device.GetAddress(), numeric_value));
+    synchronize();
+  }
+
+  void ReceiveSimplePairingComplete(hci::ErrorCode status, hci::AddressWithType device) {
+    hci_layer_->IncomingEvent(hci::SimplePairingCompleteBuilder::Create(status, device.GetAddress()));
+    synchronize();
+  }
+
+  void ReceiveLinkKeyNotification(hci::AddressWithType device, std::array<uint8_t, 16> link_key,
+                                  hci::KeyType key_type) {
+    hci_layer_->IncomingEvent(hci::LinkKeyNotificationBuilder::Create(device.GetAddress(), link_key, key_type));
+    synchronize();
   }
 
   TestModuleRegistry fake_registry_;
@@ -149,6 +197,9 @@
   channel::SecurityManagerChannel* channel_ = nullptr;
   pairing::ClassicPairingHandler* pairing_handler_ = nullptr;
   std::shared_ptr<record::SecurityRecord> security_record_ = nullptr;
+  UI* user_interface_;
+  os::Handler* user_interface_handler_;
+  l2cap::classic::SecurityInterface* security_interface_ = nullptr;
 };
 
 // Security Manager Boot Sequence (Required for SSP, these are already set at boot time)
@@ -159,8 +210,8 @@
 /*** Locally initiated ***/
 // Security Pairing Sequence (JustWorks)
 //  -> *Establish L2CAP connection*
-//  -> AuthenticationRequested (should L2CAP request secure service which causes this?)
-//  <- LinkKeyRequest
+//  -> AuthenticationRequested (L2CAP handles this)
+//  <- LinkKeyRequest   // This is entry point for remote initiated
 //  -> LinkKeyRequestNegativeReply
 //  <- IoCapabilityRequest
 //  -> IoCapabilityRequestReply
@@ -174,45 +225,20 @@
 //  <- EncryptionChange
 //  -> L2capConnectionResponse (if triggered by L2cap connection request)
 
-void ReceiveLinkKeyRequest(FakeHciLayer* hci_layer, hci::AddressWithType device) {
-  hci_layer->IncomingEvent(hci::LinkKeyRequestBuilder::Create(device.GetAddress()));
-}
-
-void ReceiveIoCapabilityRequest(FakeHciLayer* hci_layer, hci::AddressWithType device) {
-  hci_layer->IncomingEvent(hci::IoCapabilityRequestBuilder::Create(device.GetAddress()));
-}
-
-void ReceiveIoCapabilityResponse(FakeHciLayer* hci_layer, hci::AddressWithType device, hci::IoCapability io_cap,
-                                 hci::OobDataPresent oob_present, hci::AuthenticationRequirements auth_reqs) {
-  hci_layer->IncomingEvent(
-      hci::IoCapabilityResponseBuilder::Create(device.GetAddress(), io_cap, oob_present, auth_reqs));
-}
-
-void ReceiveUserConfirmationRequest(FakeHciLayer* hci_layer, hci::AddressWithType device, uint32_t numeric_value) {
-  hci_layer->IncomingEvent(hci::UserConfirmationRequestBuilder::Create(device.GetAddress(), numeric_value));
-}
-
-void ReceiveSimplePairingComplete(FakeHciLayer* hci_layer, hci::ErrorCode status, hci::AddressWithType device) {
-  hci_layer->IncomingEvent(hci::SimplePairingCompleteBuilder::Create(status, device.GetAddress()));
-}
-
-void ReceiveLinkKeyNotification(FakeHciLayer* hci_layer, hci::AddressWithType device, std::array<uint8_t, 16> link_key,
-                                hci::KeyType key_type) {
-  hci_layer->IncomingEvent(hci::LinkKeyNotificationBuilder::Create(device.GetAddress(), link_key, key_type));
-}
-
 hci::SecurityCommandView GetLastCommand(FakeHciLayer* hci_layer) {
   auto last_command = std::move(hci_layer->GetLastCommand()->command);
   auto command_packet = GetPacketView(std::move(last_command));
   auto command_packet_view = hci::CommandPacketView::Create(command_packet);
-  ASSERT(command_packet_view.IsValid());
   auto security_command_view = hci::SecurityCommandView::Create(command_packet_view);
-  ASSERT(security_command_view.IsValid());
+  if (!security_command_view.IsValid()) {
+    LOG_ERROR("Invalid security command received");
+  }
   return security_command_view;
 }
 
 TEST_F(ClassicPairingHandlerTest, setup_teardown) {}
 
+/*** JustWorks (Numeric Comparison w/ no UI) ***/
 // display_only + display_only is JustWorks no confirmation
 // Needs dialog as per security a bug unless pairing is temporary
 TEST_F(ClassicPairingHandlerTest, locally_initiatied_display_only_display_only_temp) {
@@ -220,12 +246,12 @@
   hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
   pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
                              injected_authentication_requirements);
-  ReceiveLinkKeyRequest(hci_layer_, device_);
+  ReceiveLinkKeyRequest(device_);
   auto security_command_view = GetLastCommand(hci_layer_);
   auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
   ASSERT_TRUE(link_key_neg_reply.IsValid());
   ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
-  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  ReceiveIoCapabilityRequest(device_);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
   auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
@@ -233,20 +259,22 @@
   ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
   ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
   ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
-  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::DISPLAY_ONLY, hci::OobDataPresent::NOT_PRESENT,
+  ReceiveIoCapabilityResponse(device_, hci::IoCapability::DISPLAY_ONLY, hci::OobDataPresent::NOT_PRESENT,
                               hci::AuthenticationRequirements::NO_BONDING);
   uint32_t numeric_value = 0x123;
-  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  ReceiveUserConfirmationRequest(device_, numeric_value);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
   auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
   ASSERT_TRUE(user_conf_request_reply.IsValid());
-  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  ReceiveSimplePairingComplete(hci::ErrorCode::SUCCESS, device_);
   std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
   hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
-  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ReceiveLinkKeyNotification(device_, link_key, key_type);
   ASSERT_EQ(link_key, security_record_->GetLinkKey());
   ASSERT_EQ(key_type, security_record_->GetKeyType());
+  ASSERT_FALSE(security_record_->IsAuthenticated());
+  ASSERT_FALSE(security_record_->RequiresMitmProtection());
 }
 
 // display_only + display_yes_no is JustWorks no confirmation
@@ -256,12 +284,12 @@
   hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
   pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
                              injected_authentication_requirements);
-  ReceiveLinkKeyRequest(hci_layer_, device_);
+  ReceiveLinkKeyRequest(device_);
   auto security_command_view = GetLastCommand(hci_layer_);
   auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
   ASSERT_TRUE(link_key_neg_reply.IsValid());
   ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
-  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  ReceiveIoCapabilityRequest(device_);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
   auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
@@ -269,20 +297,22 @@
   ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
   ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
   ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
-  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::DISPLAY_YES_NO, hci::OobDataPresent::NOT_PRESENT,
+  ReceiveIoCapabilityResponse(device_, hci::IoCapability::DISPLAY_YES_NO, hci::OobDataPresent::NOT_PRESENT,
                               hci::AuthenticationRequirements::NO_BONDING);
   uint32_t numeric_value = 0x123;
-  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  ReceiveUserConfirmationRequest(device_, numeric_value);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
   auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
   ASSERT_TRUE(user_conf_request_reply.IsValid());
-  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  ReceiveSimplePairingComplete(hci::ErrorCode::SUCCESS, device_);
   std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
   hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
-  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ReceiveLinkKeyNotification(device_, link_key, key_type);
   ASSERT_EQ(link_key, security_record_->GetLinkKey());
   ASSERT_EQ(key_type, security_record_->GetKeyType());
+  ASSERT_FALSE(security_record_->IsAuthenticated());
+  ASSERT_FALSE(security_record_->RequiresMitmProtection());
 }
 
 // display_only + no_input_no_output is JustWorks no confirmation
@@ -292,12 +322,12 @@
   hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
   pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
                              injected_authentication_requirements);
-  ReceiveLinkKeyRequest(hci_layer_, device_);
+  ReceiveLinkKeyRequest(device_);
   auto security_command_view = GetLastCommand(hci_layer_);
   auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
   ASSERT_TRUE(link_key_neg_reply.IsValid());
   ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
-  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  ReceiveIoCapabilityRequest(device_);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
   auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
@@ -305,20 +335,22 @@
   ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
   ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
   ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
-  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::NO_INPUT_NO_OUTPUT,
-                              hci::OobDataPresent::NOT_PRESENT, hci::AuthenticationRequirements::NO_BONDING);
+  ReceiveIoCapabilityResponse(device_, hci::IoCapability::NO_INPUT_NO_OUTPUT, hci::OobDataPresent::NOT_PRESENT,
+                              hci::AuthenticationRequirements::NO_BONDING);
   uint32_t numeric_value = 0x123;
-  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  ReceiveUserConfirmationRequest(device_, numeric_value);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
   auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
   ASSERT_TRUE(user_conf_request_reply.IsValid());
-  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  ReceiveSimplePairingComplete(hci::ErrorCode::SUCCESS, device_);
   std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
   hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
-  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ReceiveLinkKeyNotification(device_, link_key, key_type);
   ASSERT_EQ(link_key, security_record_->GetLinkKey());
   ASSERT_EQ(key_type, security_record_->GetKeyType());
+  ASSERT_FALSE(security_record_->IsAuthenticated());
+  ASSERT_FALSE(security_record_->RequiresMitmProtection());
 }
 
 // keyboard_only + no_input_no_output is JustWorks no confirmation
@@ -328,12 +360,12 @@
   hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
   pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
                              injected_authentication_requirements);
-  ReceiveLinkKeyRequest(hci_layer_, device_);
+  ReceiveLinkKeyRequest(device_);
   auto security_command_view = GetLastCommand(hci_layer_);
   auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
   ASSERT_TRUE(link_key_neg_reply.IsValid());
   ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
-  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  ReceiveIoCapabilityRequest(device_);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
   auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
@@ -341,20 +373,22 @@
   ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
   ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
   ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
-  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::NO_INPUT_NO_OUTPUT,
-                              hci::OobDataPresent::NOT_PRESENT, hci::AuthenticationRequirements::NO_BONDING);
+  ReceiveIoCapabilityResponse(device_, hci::IoCapability::NO_INPUT_NO_OUTPUT, hci::OobDataPresent::NOT_PRESENT,
+                              hci::AuthenticationRequirements::NO_BONDING);
   uint32_t numeric_value = 0x123;
-  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  ReceiveUserConfirmationRequest(device_, numeric_value);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
   auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
   ASSERT_TRUE(user_conf_request_reply.IsValid());
-  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  ReceiveSimplePairingComplete(hci::ErrorCode::SUCCESS, device_);
   std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
   hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
-  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ReceiveLinkKeyNotification(device_, link_key, key_type);
   ASSERT_EQ(link_key, security_record_->GetLinkKey());
   ASSERT_EQ(key_type, security_record_->GetKeyType());
+  ASSERT_FALSE(security_record_->IsAuthenticated());
+  ASSERT_FALSE(security_record_->RequiresMitmProtection());
 }
 
 // no_input_no_output + display_only is JustWorks no confirmation
@@ -364,12 +398,12 @@
   hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
   pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
                              injected_authentication_requirements);
-  ReceiveLinkKeyRequest(hci_layer_, device_);
+  ReceiveLinkKeyRequest(device_);
   auto security_command_view = GetLastCommand(hci_layer_);
   auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
   ASSERT_TRUE(link_key_neg_reply.IsValid());
   ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
-  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  ReceiveIoCapabilityRequest(device_);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
   auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
@@ -377,20 +411,22 @@
   ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
   ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
   ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
-  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::DISPLAY_ONLY, hci::OobDataPresent::NOT_PRESENT,
+  ReceiveIoCapabilityResponse(device_, hci::IoCapability::DISPLAY_ONLY, hci::OobDataPresent::NOT_PRESENT,
                               hci::AuthenticationRequirements::NO_BONDING);
   uint32_t numeric_value = 0x123;
-  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  ReceiveUserConfirmationRequest(device_, numeric_value);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
   auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
   ASSERT_TRUE(user_conf_request_reply.IsValid());
-  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  ReceiveSimplePairingComplete(hci::ErrorCode::SUCCESS, device_);
   std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
   hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
-  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ReceiveLinkKeyNotification(device_, link_key, key_type);
   ASSERT_EQ(link_key, security_record_->GetLinkKey());
   ASSERT_EQ(key_type, security_record_->GetKeyType());
+  ASSERT_FALSE(security_record_->IsAuthenticated());
+  ASSERT_FALSE(security_record_->RequiresMitmProtection());
 }
 
 // no_input_no_output + display_yes_no is JustWorks no confirmation
@@ -400,12 +436,12 @@
   hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
   pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
                              injected_authentication_requirements);
-  ReceiveLinkKeyRequest(hci_layer_, device_);
+  ReceiveLinkKeyRequest(device_);
   auto security_command_view = GetLastCommand(hci_layer_);
   auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
   ASSERT_TRUE(link_key_neg_reply.IsValid());
   ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
-  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  ReceiveIoCapabilityRequest(device_);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
   auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
@@ -413,20 +449,22 @@
   ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
   ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
   ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
-  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::DISPLAY_YES_NO, hci::OobDataPresent::NOT_PRESENT,
+  ReceiveIoCapabilityResponse(device_, hci::IoCapability::DISPLAY_YES_NO, hci::OobDataPresent::NOT_PRESENT,
                               hci::AuthenticationRequirements::NO_BONDING);
   uint32_t numeric_value = 0x123;
-  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  ReceiveUserConfirmationRequest(device_, numeric_value);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
   auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
   ASSERT_TRUE(user_conf_request_reply.IsValid());
-  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  ReceiveSimplePairingComplete(hci::ErrorCode::SUCCESS, device_);
   std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
   hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
-  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ReceiveLinkKeyNotification(device_, link_key, key_type);
   ASSERT_EQ(link_key, security_record_->GetLinkKey());
   ASSERT_EQ(key_type, security_record_->GetKeyType());
+  ASSERT_FALSE(security_record_->IsAuthenticated());
+  ASSERT_FALSE(security_record_->RequiresMitmProtection());
 }
 
 // no_input_no_output + keyboard_only is JustWorks no confirmation
@@ -436,12 +474,12 @@
   hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
   pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
                              injected_authentication_requirements);
-  ReceiveLinkKeyRequest(hci_layer_, device_);
+  ReceiveLinkKeyRequest(device_);
   auto security_command_view = GetLastCommand(hci_layer_);
   auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
   ASSERT_TRUE(link_key_neg_reply.IsValid());
   ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
-  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  ReceiveIoCapabilityRequest(device_);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
   auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
@@ -449,20 +487,22 @@
   ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
   ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
   ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
-  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::KEYBOARD_ONLY, hci::OobDataPresent::NOT_PRESENT,
+  ReceiveIoCapabilityResponse(device_, hci::IoCapability::KEYBOARD_ONLY, hci::OobDataPresent::NOT_PRESENT,
                               hci::AuthenticationRequirements::NO_BONDING);
   uint32_t numeric_value = 0x123;
-  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  ReceiveUserConfirmationRequest(device_, numeric_value);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
   auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
   ASSERT_TRUE(user_conf_request_reply.IsValid());
-  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  ReceiveSimplePairingComplete(hci::ErrorCode::SUCCESS, device_);
   std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
   hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
-  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ReceiveLinkKeyNotification(device_, link_key, key_type);
   ASSERT_EQ(link_key, security_record_->GetLinkKey());
   ASSERT_EQ(key_type, security_record_->GetKeyType());
+  ASSERT_FALSE(security_record_->IsAuthenticated());
+  ASSERT_FALSE(security_record_->RequiresMitmProtection());
 }
 
 // no_input_no_output + no_input_no_output is JustWorks no confirmation
@@ -472,12 +512,12 @@
   hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
   pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
                              injected_authentication_requirements);
-  ReceiveLinkKeyRequest(hci_layer_, device_);
+  ReceiveLinkKeyRequest(device_);
   auto security_command_view = GetLastCommand(hci_layer_);
   auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
   ASSERT_TRUE(link_key_neg_reply.IsValid());
   ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
-  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  ReceiveIoCapabilityRequest(device_);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
   auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
@@ -485,22 +525,36 @@
   ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
   ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
   ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
-  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::NO_INPUT_NO_OUTPUT,
-                              hci::OobDataPresent::NOT_PRESENT, hci::AuthenticationRequirements::NO_BONDING);
+  ReceiveIoCapabilityResponse(device_, hci::IoCapability::NO_INPUT_NO_OUTPUT, hci::OobDataPresent::NOT_PRESENT,
+                              hci::AuthenticationRequirements::NO_BONDING);
   uint32_t numeric_value = 0x123;
-  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  ReceiveUserConfirmationRequest(device_, numeric_value);
   security_command_view = GetLastCommand(hci_layer_);
   ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
   auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
   ASSERT_TRUE(user_conf_request_reply.IsValid());
-  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  ReceiveSimplePairingComplete(hci::ErrorCode::SUCCESS, device_);
   std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
   hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
-  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ReceiveLinkKeyNotification(device_, link_key, key_type);
   ASSERT_EQ(link_key, security_record_->GetLinkKey());
   ASSERT_EQ(key_type, security_record_->GetKeyType());
+  ASSERT_FALSE(security_record_->IsAuthenticated());
+  ASSERT_FALSE(security_record_->RequiresMitmProtection());
 }
 
+/*** Numeric Comparison ***/
+// display_yes_no + display_only
+
+// display_yes_no + display_yes_no
+// display_yes_no + keyboard_only
+// display_yes_no + no_input_no_output
+
+// keyboard_only + display_only
+// keyboard_only + display_yes_no
+
+// keyboard_only + keyboard_only  (a just works I missed)
+
 // Remotely initiated
 
 // Collisions
diff --git a/gd/security/pairing/pairing_handler.h b/gd/security/pairing/pairing_handler.h
index 886b1a4..8047147 100644
--- a/gd/security/pairing/pairing_handler.h
+++ b/gd/security/pairing/pairing_handler.h
@@ -24,6 +24,7 @@
 #include "security/channel/security_manager_channel.h"
 #include "security/record/security_record.h"
 #include "security/smp_packets.h"
+#include "security/ui.h"
 
 namespace bluetooth {
 namespace security {
@@ -34,7 +35,7 @@
  *
  * <p>Extend this class in order to implement a new style of pairing.
  */
-class PairingHandler {
+class PairingHandler : public UICallbacks {
  public:
   PairingHandler(channel::SecurityManagerChannel* security_manager_channel,
                  std::shared_ptr<record::SecurityRecord> record)
@@ -62,6 +63,10 @@
   virtual void OnReceive(hci::UserConfirmationRequestView packet) = 0;
   virtual void OnReceive(hci::UserPasskeyRequestView packet) = 0;
 
+  virtual void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) = 0;
+  virtual void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) = 0;
+  virtual void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) = 0;
+
  protected:
   std::shared_ptr<record::SecurityRecord> GetRecord() {
     return record_;
diff --git a/gd/security/pairing_handler_le.cc b/gd/security/pairing_handler_le.cc
index 0484971..12cf967 100644
--- a/gd/security/pairing_handler_le.cc
+++ b/gd/security/pairing_handler_le.cc
@@ -18,15 +18,27 @@
 
 #include "security/pairing_handler_le.h"
 
+#include "os/rand.h"
+
 namespace bluetooth {
 namespace security {
 
+MyOobData PairingHandlerLe::GenerateOobData() {
+  MyOobData data{};
+  std::tie(data.private_key, data.public_key) = GenerateECDHKeyPair();
+
+  data.r = bluetooth::os::GenerateRandom<16>();
+  data.c = crypto_toolbox::f4(data.public_key.x.data(), data.public_key.x.data(), data.r, 0);
+  return data;
+}
+
 void PairingHandlerLe::PairingMain(InitialInformations i) {
   LOG_INFO("Pairing Started");
 
   if (i.remotely_initiated) {
     LOG_INFO("Was remotely initiated, presenting user with the accept prompt");
-    i.ui_handler->DisplayPairingPrompt(i.remote_connection_address, i.remote_name);
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayPairingPrompt, common::Unretained(i.user_interface),
+                                                    i.remote_connection_address, i.remote_name));
 
     // If pairing was initiated by remote device, wait for the user to accept
     // the request from the UI.
@@ -34,7 +46,8 @@
     std::optional<PairingEvent> pairingAccepted = WaitUiPairingAccept();
     if (!pairingAccepted || pairingAccepted->ui_value == 0) {
       LOG_INFO("User either did not accept the remote pairing, or the prompt timed out");
-      SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
+      // TODO: Uncomment this one once we find a way to attempt to send packet when the link is down
+      // SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
       i.OnPairingFinished(PairingFailure("User either did not accept the remote pairing, or the prompt timed out"));
       return;
     }
@@ -56,7 +69,8 @@
   auto [pairing_request, pairing_response] = std::get<Phase1Result>(phase_1_result);
 
   /************************************************ PHASE 2 *********************************************************/
-  if (pairing_request.GetAuthReq() & pairing_response.GetAuthReq() & AuthReqMaskSc) {
+  bool isSecureConnections = pairing_request.GetAuthReq() & pairing_response.GetAuthReq() & AuthReqMaskSc;
+  if (isSecureConnections) {
     // 2.3.5.6 LE Secure Connections pairing phase 2
     LOG_INFO("Pairing Phase 2 LE Secure connections Started");
 
@@ -97,6 +111,7 @@
 
     Octet16 ltk = std::get<Octet16>(stage_2_result);
     if (IAmMaster(i)) {
+      LOG_INFO("Sending start encryption request");
       SendHciLeStartEncryption(i, i.connection_handle, {0}, {0}, ltk);
     }
 
@@ -121,14 +136,15 @@
 
     Octet16 stk = std::get<Octet16>(stage2result);
     if (IAmMaster(i)) {
+      LOG_INFO("Sending start encryption request");
       SendHciLeStartEncryption(i, i.connection_handle, {0}, {0}, stk);
     }
   }
 
   /************************************************ PHASE 3 *********************************************************/
+  LOG_INFO("Waiting for encryption changed");
   auto encryption_change_result = WaitEncryptionChanged();
   if (std::holds_alternative<PairingFailure>(encryption_change_result)) {
-    LOG_ERROR("encryption change failed");
     i.OnPairingFinished(std::get<PairingFailure>(encryption_change_result));
     return;
   } else if (std::holds_alternative<EncryptionChangeView>(encryption_change_result)) {
@@ -136,28 +152,44 @@
     if (encryption_changed.GetStatus() != hci::ErrorCode::SUCCESS ||
         encryption_changed.GetEncryptionEnabled() != hci::EncryptionEnabled::ON) {
       i.OnPairingFinished(PairingFailure("Encryption change failed"));
+      return;
     }
   } else if (std::holds_alternative<EncryptionKeyRefreshCompleteView>(encryption_change_result)) {
     EncryptionKeyRefreshCompleteView encryption_changed =
         std::get<EncryptionKeyRefreshCompleteView>(encryption_change_result);
     if (encryption_changed.GetStatus() != hci::ErrorCode::SUCCESS) {
       i.OnPairingFinished(PairingFailure("Encryption key refresh failed"));
+      return;
     }
   } else {
     i.OnPairingFinished(PairingFailure("Unknown case of encryption change result"));
+    return;
   }
+  LOG_INFO("Encryption change finished successfully");
 
-  DistributedKeysOrFailure keyExchangeStatus = DistributeKeys(i, pairing_response);
+  DistributedKeysOrFailure keyExchangeStatus = DistributeKeys(i, pairing_response, isSecureConnections);
   if (std::holds_alternative<PairingFailure>(keyExchangeStatus)) {
     i.OnPairingFinished(std::get<PairingFailure>(keyExchangeStatus));
     LOG_ERROR("Key exchange failed");
     return;
   }
 
+  // If it's secure connections pairing, do cross-transport key derivation
+  DistributedKeys distributed_keys = std::get<DistributedKeys>(keyExchangeStatus);
+  if ((pairing_response.GetAuthReq() & AuthReqMaskSc) && distributed_keys.ltk.has_value()) {
+    bool use_h7 = (pairing_response.GetAuthReq() & AuthReqMaskCt2);
+    Octet16 link_key = crypto_toolbox::ltk_to_link_key(*(distributed_keys.ltk), use_h7);
+    distributed_keys.link_key = link_key;
+  }
+
+  // bool bonding = pairing_request.GetAuthReq() & pairing_response.GetAuthReq() & AuthReqMaskBondingFlag;
+
   i.OnPairingFinished(PairingResult{
       .connection_address = i.remote_connection_address,
       .distributed_keys = std::get<DistributedKeys>(keyExchangeStatus),
   });
+
+  LOG_INFO("Pairing finished successfully.");
 }
 
 Phase1ResultOrFailure PairingHandlerLe::ExchangePairingFeature(const InitialInformations& i) {
@@ -259,16 +291,22 @@
 }
 
 DistributedKeysOrFailure PairingHandlerLe::DistributeKeys(const InitialInformations& i,
-                                                          const PairingResponseView& pairing_response) {
-  LOG_INFO("Key distribution start");
-
-  const uint8_t& keys_i_receive =
+                                                          const PairingResponseView& pairing_response,
+                                                          bool isSecureConnections) {
+  uint8_t keys_i_receive =
       IAmMaster(i) ? pairing_response.GetResponderKeyDistribution() : pairing_response.GetInitiatorKeyDistribution();
-  const uint8_t& keys_i_send =
+  uint8_t keys_i_send =
       IAmMaster(i) ? pairing_response.GetInitiatorKeyDistribution() : pairing_response.GetResponderKeyDistribution();
 
-  // TODO: obtain actual values!
+  // In Secure Connections on the LE Transport, the EncKey field shall be ignored
+  if (isSecureConnections) {
+    keys_i_send = (~KeyMaskEnc) & keys_i_send;
+    keys_i_receive = (~KeyMaskEnc) & keys_i_receive;
+  }
 
+  LOG_INFO("Key distribution start, keys_i_send=0x%02x, keys_i_receive=0x%02x", keys_i_send, keys_i_receive);
+
+  // TODO: obtain actual values!
   Octet16 my_ltk = {0};
   uint16_t my_ediv{0};
   std::array<uint8_t, 8> my_rand = {0};
@@ -306,8 +344,7 @@
   std::optional<Octet16> ltk;                 /* Legacy only */
   std::optional<uint16_t> ediv;               /* Legacy only */
   std::optional<std::array<uint8_t, 8>> rand; /* Legacy only */
-  std::optional<Address> identity_address;
-  AddrType identity_address_type;
+  std::optional<hci::AddressWithType> identity_address;
   std::optional<Octet16> irk;
   std::optional<Octet16> signature_key;
 
@@ -350,8 +387,10 @@
       return std::get<PairingFailure>(iapacket);
     }
     LOG_INFO("Received Identity Address Information");
-    identity_address = std::get<IdentityAddressInformationView>(iapacket).GetBdAddr();
-    identity_address_type = std::get<IdentityAddressInformationView>(iapacket).GetAddrType();
+    auto iapacketview = std::get<IdentityAddressInformationView>(iapacket);
+    identity_address = hci::AddressWithType(iapacketview.GetBdAddr(), iapacketview.GetAddrType() == AddrType::PUBLIC
+                                                                          ? hci::AddressType::PUBLIC_DEVICE_ADDRESS
+                                                                          : hci::AddressType::RANDOM_DEVICE_ADDRESS);
   }
 
   if (keys_i_receive & KeyMaskSign) {
@@ -365,24 +404,33 @@
     signature_key = std::get<SigningInformationView>(packet).GetSignatureKey();
   }
 
-  return DistributedKeys{ltk, ediv, rand, identity_address, identity_address_type, irk, signature_key};
+  return DistributedKeys{.ltk = ltk,
+                         .ediv = ediv,
+                         .rand = rand,
+                         .identity_address = identity_address,
+                         .irk = irk,
+                         .signature_key = signature_key};
 }
 
 void PairingHandlerLe::SendKeys(const InitialInformations& i, const uint8_t& keys_i_send, Octet16 ltk, uint16_t ediv,
                                 std::array<uint8_t, 8> rand, Octet16 irk, Address identity_address,
                                 AddrType identity_addres_type, Octet16 signature_key) {
   if (keys_i_send & KeyMaskEnc) {
+    LOG_INFO("Sending Encryption Information");
     SendL2capPacket(i, EncryptionInformationBuilder::Create(ltk));
+    LOG_INFO("Sending Master Identification");
     SendL2capPacket(i, MasterIdentificationBuilder::Create(ediv, rand));
   }
 
   if (keys_i_send & KeyMaskId) {
+    LOG_INFO("Sending Identity Information");
     SendL2capPacket(i, IdentityInformationBuilder::Create(irk));
-
+    LOG_INFO("Sending Identity Address Information");
     SendL2capPacket(i, IdentityAddressInformationBuilder::Create(identity_addres_type, identity_address));
   }
 
   if (keys_i_send & KeyMaskSign) {
+    LOG_INFO("Sending Signing Information");
     SendL2capPacket(i, SigningInformationBuilder::Create(signature_key));
   }
 }
diff --git a/gd/security/pairing_handler_le.h b/gd/security/pairing_handler_le.h
index 4c7fc0c..31abba0 100644
--- a/gd/security/pairing_handler_le.h
+++ b/gd/security/pairing_handler_le.h
@@ -41,10 +41,10 @@
 // Code generated by PDL does not allow us ot do || and && operations on bits
 // efficiently. Use those masks on fields requiring them until this is solved
 constexpr uint8_t AuthReqMaskBondingFlag = 0x01;
-constexpr uint8_t AuthReqMaskMitm = 0x02;
-constexpr uint8_t AuthReqMaskSc = 0x04;
-constexpr uint8_t AuthReqMaskKeypress = 0x08;
-constexpr uint8_t AuthReqMaskCt2 = 0x10;
+constexpr uint8_t AuthReqMaskMitm = 0x04;
+constexpr uint8_t AuthReqMaskSc = 0x08;
+constexpr uint8_t AuthReqMaskKeypress = 0x10;
+constexpr uint8_t AuthReqMaskCt2 = 0x20;
 
 constexpr uint8_t KeyMaskEnc = 0x01;
 constexpr uint8_t KeyMaskId = 0x02;
@@ -136,11 +136,13 @@
   void SendHciLeStartEncryption(const InitialInformations& i, uint16_t conn_handle, const std::array<uint8_t, 8>& rand,
                                 const uint16_t& ediv, const Octet16& ltk) {
     i.le_security_interface->EnqueueCommand(hci::LeStartEncryptionBuilder::Create(conn_handle, rand, ediv, ltk),
-                                            common::BindOnce([](hci::CommandStatusView) {
+                                            i.l2cap_handler->BindOnce([](hci::CommandStatusView) {
                                               // TODO: handle command status. It's important - can show we are not
                                               // connected any more.
-                                            }),
-                                            nullptr);
+
+                                              // TODO: if anything useful must be done there, use some sort of proper
+                                              // handler, wait/notify, and execute on the handler thread
+                                            }));
   }
 
   std::variant<PairingFailure, EncryptionChangeView, EncryptionKeyRefreshCompleteView> WaitEncryptionChanged() {
@@ -152,7 +154,7 @@
     if (e.hci_event->GetEventCode() == hci::EventCode::ENCRYPTION_CHANGE) {
       EncryptionChangeView enc_chg_packet = EncryptionChangeView::Create(*e.hci_event);
       if (!enc_chg_packet.IsValid()) {
-        return PairingFailure("Invalid EncryptionChange packet received");
+        return PairingFailure("Invalid Encryption Change packet received");
       }
       return enc_chg_packet;
     }
@@ -160,7 +162,7 @@
     if (e.hci_event->GetEventCode() == hci::EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE) {
       hci::EncryptionKeyRefreshCompleteView enc_packet = EncryptionKeyRefreshCompleteView::Create(*e.hci_event);
       if (!enc_packet.IsValid()) {
-        return PairingFailure("Invalid EncryptionChange packet received");
+        return PairingFailure("Invalid Key Refresh packet received");
       }
       return enc_packet;
     }
@@ -174,14 +176,7 @@
 
   /* This function generates data that should be passed to remote device, except
      the private key. */
-  static MyOobData GenerateOobData() {
-    MyOobData data;
-    std::tie(data.private_key, data.public_key) = GenerateECDHKeyPair();
-
-    data.r = GenerateRandom<16>();
-    data.c = crypto_toolbox::f4(data.public_key.x.data(), data.public_key.x.data(), data.r, 0);
-    return data;
-  }
+  static MyOobData GenerateOobData();
 
   std::variant<PairingFailure, KeyExchangeResult> ExchangePublicKeys(const InitialInformations& i,
                                                                      OobDataFlag remote_have_oob_data);
@@ -210,7 +205,8 @@
                                                   const Stage1Result stage1result,
                                                   const std::array<uint8_t, 32>& dhkey);
 
-  DistributedKeysOrFailure DistributeKeys(const InitialInformations& i, const PairingResponseView& pairing_response);
+  DistributedKeysOrFailure DistributeKeys(const InitialInformations& i, const PairingResponseView& pairing_response,
+                                          bool isSecureConnections);
 
   DistributedKeysOrFailure ReceiveKeys(const uint8_t& keys_i_receive);
 
@@ -459,23 +455,6 @@
     return WaitPacket<Code::SIGNING_INFORMATION>();
   }
 
-  template <size_t SIZE>
-  static std::array<uint8_t, SIZE> GenerateRandom() {
-    // TODO:  We need a proper  random number generator here.
-    // use current time as seed for random generator
-    std::srand(std::time(nullptr));
-    std::array<uint8_t, SIZE> r;
-    for (size_t i = 0; i < SIZE; i++) r[i] = std::rand();
-    return r;
-  }
-
-  uint32_t GenerateRandom() {
-    // TODO:  We need a proper  random number generator here.
-    // use current time as seed for random generator
-    std::srand(std::time(nullptr));
-    return std::rand();
-  }
-
   /* This is just for test, never use in production code! */
   void WaitUntilPairingFinished() {
     thread_.join();
@@ -490,4 +469,4 @@
   std::thread thread_;
 };
 }  // namespace security
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/security/pairing_handler_le_legacy.cc b/gd/security/pairing_handler_le_legacy.cc
index 0c3a73b..a915c61 100644
--- a/gd/security/pairing_handler_le_legacy.cc
+++ b/gd/security/pairing_handler_le_legacy.cc
@@ -18,6 +18,10 @@
 
 #include "security/pairing_handler_le.h"
 
+#include "os/rand.h"
+
+using bluetooth::os::GenerateRandom;
+
 namespace bluetooth {
 namespace security {
 
@@ -89,9 +93,12 @@
     constexpr uint32_t PASSKEY_MAX = 999999;
     if (passkey > PASSKEY_MAX) passkey >>= 1;
 
-    i.ui_handler->DisplayConfirmValue(passkey);
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayConfirmValue, common::Unretained(i.user_interface),
+                                                    i.remote_connection_address, i.remote_name, passkey));
   } else {
-    i.ui_handler->DisplayEnterPasskeyDialog();
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayEnterPasskeyDialog,
+                                                    common::Unretained(i.user_interface), i.remote_connection_address,
+                                                    i.remote_name));
     std::optional<PairingEvent> response = WaitUiPasskey();
     if (!response) return PairingFailure("Passkey did not arrive!");
 
@@ -124,17 +131,16 @@
 
     // LOG(INFO) << +(IAmMaster(i)) << " tk = " << base::HexEncode(tk.data(), tk.size());
     // LOG(INFO) << +(IAmMaster(i)) << " mrand = " << base::HexEncode(mrand.data(), mrand.size());
-    // LOG(INFO) << +(IAmMaster(i)) << " preq = " << base::HexEncode(preq.data(), preq.size());
     // LOG(INFO) << +(IAmMaster(i)) << " pres = " << base::HexEncode(pres.data(), pres.size());
-    // LOG(INFO) << +(IAmMaster(i)) << " i.remote_connection_address_type = " << +i.remote_connection_address_type;
-    // LOG(INFO) << +(IAmMaster(i)) << " i.i.remote_connection_address.address = " << i.remote_connection_address;
-    // LOG(INFO) << +(IAmMaster(i)) << " i.my_connection_address_type = " << +i.my_connection_address_type;
-    // LOG(INFO) << +(IAmMaster(i)) << " i.i.my_connection_address.address = " << i.my_connection_address;
+    // LOG(INFO) << +(IAmMaster(i)) << " preq = " << base::HexEncode(preq.data(), preq.size());
+
     Octet16 mconfirm = crypto_toolbox::c1(
         tk, mrand, preq.data(), pres.data(), (uint8_t)i.my_connection_address.GetAddressType(),
         i.my_connection_address.GetAddress().address, (uint8_t)i.remote_connection_address.GetAddressType(),
         i.remote_connection_address.GetAddress().address);
 
+    // LOG(INFO) << +(IAmMaster(i)) << " mconfirm = " << base::HexEncode(mconfirm.data(), mconfirm.size());
+
     LOG_INFO("Master sends Mconfirm");
     SendL2capPacket(i, PairingConfirmBuilder::Create(mconfirm));
 
@@ -155,14 +161,8 @@
     }
     srand = std::get<PairingRandomView>(random_pkt).GetRandomValue();
 
-    // LOG(INFO) << +(IAmMaster(i)) << " tk = " << base::HexEncode(tk.data(), tk.size());
     // LOG(INFO) << +(IAmMaster(i)) << " srand = " << base::HexEncode(srand.data(), srand.size());
-    // LOG(INFO) << +(IAmMaster(i)) << " preq = " << base::HexEncode(preq.data(), preq.size());
-    // LOG(INFO) << +(IAmMaster(i)) << " pres = " << base::HexEncode(pres.data(), pres.size());
-    // LOG(INFO) << +(IAmMaster(i)) << " i.my_connection_address_type = " << +i.my_connection_address_type;
-    // LOG(INFO) << +(IAmMaster(i)) << " i.i.my_connection_address.address = " << i.my_connection_address;
-    // LOG(INFO) << +(IAmMaster(i)) << " i.remote_connection_address_type = " << +i.remote_connection_address_type;
-    // LOG(INFO) << +(IAmMaster(i)) << " i.i.remote_connection_address.address = " << i.remote_connection_address;
+
     Octet16 sconfirm_generated = crypto_toolbox::c1(
         tk, srand, preq.data(), pres.data(), (uint8_t)i.my_connection_address.GetAddressType(),
         i.my_connection_address.GetAddress().address, (uint8_t)i.remote_connection_address.GetAddressType(),
@@ -202,14 +202,6 @@
     }
     mrand = std::get<PairingRandomView>(random_pkt).GetRandomValue();
 
-    // LOG(INFO) << +(IAmMaster(i)) << " tk = " << base::HexEncode(tk.data(), tk.size());
-    // LOG(INFO) << +(IAmMaster(i)) << " mrand = " << base::HexEncode(mrand.data(), mrand.size());
-    // LOG(INFO) << +(IAmMaster(i)) << " preq = " << base::HexEncode(preq.data(), preq.size());
-    // LOG(INFO) << +(IAmMaster(i)) << " pres = " << base::HexEncode(pres.data(), pres.size());
-    // LOG(INFO) << +(IAmMaster(i)) << " i.my_connection_address_type = " << +i.my_connection_address_type;
-    // LOG(INFO) << +(IAmMaster(i)) << " i.i.my_connection_address.address = " << i.my_connection_address;
-    // LOG(INFO) << +(IAmMaster(i)) << " i.remote_connection_address_type = " << +i.remote_connection_address_type;
-    // LOG(INFO) << +(IAmMaster(i)) << " i.i.remote_connection_address.address = " << i.remote_connection_address;
     Octet16 mconfirm_generated = crypto_toolbox::c1(
         tk, mrand, preq.data(), pres.data(), (uint8_t)i.remote_connection_address.GetAddressType(),
         i.remote_connection_address.GetAddress().address, (uint8_t)i.my_connection_address.GetAddressType(),
@@ -228,7 +220,7 @@
   LOG_INFO("Legacy stage 2 finish");
 
   /* STK */
-  return crypto_toolbox::s1(tk, srand, mrand);
+  return crypto_toolbox::s1(tk, mrand, srand);
 }
 }  // namespace security
 }  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/pairing_handler_le_secure_connections.cc b/gd/security/pairing_handler_le_secure_connections.cc
index f5b0a0f..d3f02b2 100644
--- a/gd/security/pairing_handler_le_secure_connections.cc
+++ b/gd/security/pairing_handler_le_secure_connections.cc
@@ -18,6 +18,10 @@
 
 #include "security/pairing_handler_le.h"
 
+#include "os/rand.h"
+
+using bluetooth::os::GenerateRandom;
+
 namespace bluetooth {
 namespace security {
 
@@ -85,7 +89,8 @@
                                                                   const EcdhPublicKey& PKa, const EcdhPublicKey& PKb,
                                                                   const PairingRequestView& pairing_request,
                                                                   const PairingResponseView& pairing_response) {
-  if ((pairing_request.GetAuthReq() & pairing_response.GetAuthReq() & AuthReqMaskMitm) == 0) {
+  if (((pairing_request.GetAuthReq() & AuthReqMaskMitm) == 0) &&
+      ((pairing_response.GetAuthReq() & AuthReqMaskMitm) == 0)) {
     // If both devices have not set MITM option, Just Works shall be used
     return SecureConnectionsJustWorks(i, PKa, PKb);
   }
@@ -283,10 +288,13 @@
     constexpr uint32_t PASSKEY_MAX = 999999;
     while (passkey > PASSKEY_MAX) passkey >>= 1;
 
-    i.ui_handler->DisplayPasskey(passkey);
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayPasskey, common::Unretained(i.user_interface),
+                                                    i.remote_connection_address, i.remote_name, passkey));
 
   } else if (my_iocaps == IoCapability::KEYBOARD_ONLY || remote_iocaps == IoCapability::DISPLAY_ONLY) {
-    i.ui_handler->DisplayEnterPasskeyDialog();
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayEnterPasskeyDialog,
+                                                    common::Unretained(i.user_interface), i.remote_connection_address,
+                                                    i.remote_name));
     std::optional<PairingEvent> response = WaitUiPasskey();
     if (!response) return PairingFailure("Passkey did not arrive!");
 
@@ -401,7 +409,8 @@
 
   uint32_t number_to_display = crypto_toolbox::g2((uint8_t*)PKa.x.data(), (uint8_t*)PKb.x.data(), Na, Nb);
 
-  i.ui_handler->DisplayConfirmValue(number_to_display);
+  i.user_interface_handler->Post(common::BindOnce(&UI::DisplayConfirmValue, common::Unretained(i.user_interface),
+                                                  i.remote_connection_address, i.remote_name, number_to_display));
 
   std::optional<PairingEvent> confirmyesno = WaitUiConfirmYesNo();
   if (!confirmyesno || confirmyesno->ui_value == 0) {
@@ -414,7 +423,7 @@
 
 Stage1ResultOrFailure PairingHandlerLe::SecureConnectionsJustWorks(const InitialInformations& i,
                                                                    const EcdhPublicKey& PKa, const EcdhPublicKey& PKb) {
-  Octet16 Ca, Cb, Na, Nb, ra, rb;
+  Octet16 Cb, Na, Nb, ra, rb;
 
   ra = rb = {0};
 
@@ -437,13 +446,13 @@
     }
     Nb = std::get<PairingRandomView>(random).GetRandomValue();
 
-    // Compute confirm
-    Ca = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nb, 0);
+    // Compute Cb locally
+    Octet16 Cb_local = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nb, 0);
 
-    if (Ca != Cb) {
-      LOG_INFO("Ca != Cb, aborting!");
+    if (Cb_local != Cb) {
+      LOG_INFO("Cb_local != Cb, aborting!");
       SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED));
-      return PairingFailure("Ca != Cb");
+      return PairingFailure("Cb_local != Cb");
     }
   } else {
     Nb = GenerateRandom<16>();
diff --git a/gd/security/pairing_handler_le_unittest.cc b/gd/security/pairing_handler_le_unittest.cc
index 3610700..0a1eb9e 100644
--- a/gd/security/pairing_handler_le_unittest.cc
+++ b/gd/security/pairing_handler_le_unittest.cc
@@ -21,6 +21,7 @@
 #include <memory>
 
 #include "os/log.h"
+#include "os/rand.h"
 #include "security/pairing_handler_le.h"
 #include "security/test/mocks.h"
 
@@ -29,6 +30,7 @@
 using ::testing::Field;
 using ::testing::VariantWith;
 
+using bluetooth::os::GenerateRandom;
 using bluetooth::security::CommandView;
 
 namespace bluetooth {
@@ -45,18 +47,6 @@
   return CommandView::Create(temp_cmd_view);
 }
 
-std::condition_variable outgoing_l2cap_blocker_;
-std::optional<bluetooth::security::CommandView> outgoing_l2cap_packet_;
-
-bool WaitForOutgoingL2capPacket() {
-  std::mutex mutex;
-  std::unique_lock<std::mutex> lock(mutex);
-  if (outgoing_l2cap_blocker_.wait_for(lock, std::chrono::seconds(5)) == std::cv_status::timeout) {
-    return false;
-  }
-  return true;
-}
-
 class PairingResultHandlerMock {
  public:
   MOCK_CONST_METHOD1(OnPairingFinished, void(PairingResultOrFailure));
@@ -111,11 +101,34 @@
     outgoing_l2cap_blocker_.notify_one();
   }
 
+  std::optional<bluetooth::security::CommandView> WaitForOutgoingL2capPacket() {
+    std::mutex mutex;
+    std::unique_lock<std::mutex> lock(mutex);
+
+    // It is possible that we lost wakeup from condition_variable, check if data is already waiting to be processed
+    if (outgoing_l2cap_packet_ != std::nullopt) {
+      std::optional<bluetooth::security::CommandView> tmp = std::nullopt;
+      outgoing_l2cap_packet_.swap(tmp);
+      return tmp;
+    }
+
+    // Data not ready yet, wait for it.
+    if (outgoing_l2cap_blocker_.wait_for(lock, std::chrono::seconds(5)) == std::cv_status::timeout) {
+      return std::nullopt;
+    }
+
+    std::optional<bluetooth::security::CommandView> tmp = std::nullopt;
+    outgoing_l2cap_packet_.swap(tmp);
+    return tmp;
+  }
+
  public:
   os::Thread* thread_;
   os::Handler* handler_;
   std::unique_ptr<common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>> bidi_queue_;
   std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> up_buffer_;
+  std::condition_variable outgoing_l2cap_blocker_;
+  std::optional<bluetooth::security::CommandView> outgoing_l2cap_packet_ = std::nullopt;
 };
 
 InitialInformations initial_informations{
@@ -131,7 +144,7 @@
 
     .remotely_initiated = false,
     .remote_connection_address = {{}, hci::AddressType::RANDOM_DEVICE_ADDRESS},
-    .ui_handler = &uiMock,
+    .user_interface = &uiMock,
     .le_security_interface = &leSecurityMock,
     .OnPairingFinished = OnPairingFinished,
 };
@@ -139,12 +152,14 @@
 TEST_F(PairingHandlerUnitTest, test_phase_1_failure) {
   initial_informations.proper_l2cap_interface = up_buffer_.get();
   initial_informations.l2cap_handler = handler_;
+  initial_informations.user_interface_handler = handler_;
 
   std::unique_ptr<PairingHandlerLe> pairing_handler =
       std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
 
-  EXPECT_TRUE(WaitForOutgoingL2capPacket());
-  EXPECT_EQ(outgoing_l2cap_packet_->GetCode(), Code::PAIRING_REQUEST);
+  std::optional<bluetooth::security::CommandView> pairing_request = WaitForOutgoingL2capPacket();
+  EXPECT_TRUE(pairing_request.has_value());
+  EXPECT_EQ(pairing_request->GetCode(), Code::PAIRING_REQUEST);
 
   EXPECT_CALL(*pairingResult, OnPairingFinished(VariantWith<PairingFailure>(_))).Times(1);
 
@@ -153,22 +168,25 @@
   bad_pairing_response.IsValid();
   pairing_handler->OnCommandView(bad_pairing_response);
 
-  EXPECT_TRUE(WaitForOutgoingL2capPacket());
-  EXPECT_EQ(outgoing_l2cap_packet_->GetCode(), Code::PAIRING_FAILED);
+  std::optional<bluetooth::security::CommandView> pairing_failure = WaitForOutgoingL2capPacket();
+  EXPECT_TRUE(pairing_failure.has_value());
+  EXPECT_EQ(pairing_failure->GetCode(), Code::PAIRING_FAILED);
 }
 
 TEST_F(PairingHandlerUnitTest, test_secure_connections_just_works) {
   initial_informations.proper_l2cap_interface = up_buffer_.get();
   initial_informations.l2cap_handler = handler_;
+  initial_informations.user_interface_handler = handler_;
 
   // we keep the pairing_handler as unique_ptr to better mimick how it's used
   // in the real world
   std::unique_ptr<PairingHandlerLe> pairing_handler =
       std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
 
-  EXPECT_TRUE(WaitForOutgoingL2capPacket());
-  EXPECT_EQ(outgoing_l2cap_packet_->GetCode(), Code::PAIRING_REQUEST);
-  CommandView pairing_request = outgoing_l2cap_packet_.value();
+  std::optional<bluetooth::security::CommandView> pairing_request_pkt = WaitForOutgoingL2capPacket();
+  EXPECT_TRUE(pairing_request_pkt.has_value());
+  EXPECT_EQ(pairing_request_pkt->GetCode(), Code::PAIRING_REQUEST);
+  CommandView pairing_request = pairing_request_pkt.value();
 
   auto pairing_response = BuilderToView(
       PairingResponseBuilder::Create(IoCapability::KEYBOARD_DISPLAY, OobDataFlag::NOT_PRESENT,
@@ -177,10 +195,11 @@
   // Phase 1 finished.
 
   // pairing public key
-  EXPECT_TRUE(WaitForOutgoingL2capPacket());
-  EXPECT_EQ(Code::PAIRING_PUBLIC_KEY, outgoing_l2cap_packet_->GetCode());
+  std::optional<bluetooth::security::CommandView> public_key_pkt = WaitForOutgoingL2capPacket();
+  EXPECT_TRUE(public_key_pkt.has_value());
+  EXPECT_EQ(Code::PAIRING_PUBLIC_KEY, public_key_pkt->GetCode());
   EcdhPublicKey my_public_key;
-  auto ppkv = PairingPublicKeyView::Create(outgoing_l2cap_packet_.value());
+  auto ppkv = PairingPublicKeyView::Create(public_key_pkt.value());
   ppkv.IsValid();
   my_public_key.x = ppkv.GetPublicKeyX();
   my_public_key.y = ppkv.GetPublicKeyY();
@@ -195,25 +214,21 @@
   Octet16 ra, rb;
   ra = rb = {0};
 
-  Octet16 Nb = PairingHandlerLe::GenerateRandom<16>();
+  Octet16 Nb = GenerateRandom<16>();
 
   // Compute confirm
-  Octet16 Cb = crypto_toolbox::f4((uint8_t*)my_public_key.x.data(), (uint8_t*)public_key.x.data(), Nb, 0);
+  Octet16 Cb = crypto_toolbox::f4((uint8_t*)public_key.x.data(), (uint8_t*)my_public_key.x.data(), Nb, 0);
 
   pairing_handler->OnCommandView(BuilderToView(PairingConfirmBuilder::Create(Cb)));
 
   // random
-  EXPECT_TRUE(WaitForOutgoingL2capPacket());
-  EXPECT_EQ(Code::PAIRING_RANDOM, outgoing_l2cap_packet_->GetCode());
-  auto prv = PairingRandomView::Create(outgoing_l2cap_packet_.value());
+  std::optional<bluetooth::security::CommandView> random_pkt = WaitForOutgoingL2capPacket();
+  EXPECT_TRUE(random_pkt.has_value());
+  EXPECT_EQ(Code::PAIRING_RANDOM, random_pkt->GetCode());
+  auto prv = PairingRandomView::Create(random_pkt.value());
   prv.IsValid();
   Octet16 Na = prv.GetRandomValue();
 
-  // Compute Ca, compare
-  Octet16 Ca = crypto_toolbox::f4((uint8_t*)my_public_key.x.data(), (uint8_t*)public_key.x.data(), Na, 0);
-
-  EXPECT_EQ(Ca, Cb);
-
   pairing_handler->OnCommandView(BuilderToView(PairingRandomBuilder::Create(Nb)));
 
   // Start of authentication stage 2
@@ -240,9 +255,10 @@
   Octet16 Ea = crypto_toolbox::f6(mac_key, Na, Nb, rb, iocapA.data(), a, b);
   Octet16 Eb = crypto_toolbox::f6(mac_key, Nb, Na, ra, iocapB.data(), b, a);
 
-  EXPECT_TRUE(WaitForOutgoingL2capPacket());
-  EXPECT_EQ(Code::PAIRING_DH_KEY_CHECK, outgoing_l2cap_packet_->GetCode());
-  auto pdhkcv = PairingDhKeyCheckView::Create(outgoing_l2cap_packet_.value());
+  std::optional<bluetooth::security::CommandView> dh_key_pkt = WaitForOutgoingL2capPacket();
+  EXPECT_TRUE(dh_key_pkt.has_value());
+  EXPECT_EQ(Code::PAIRING_DH_KEY_CHECK, dh_key_pkt->GetCode());
+  auto pdhkcv = PairingDhKeyCheckView::Create(dh_key_pkt.value());
   pdhkcv.IsValid();
   EXPECT_EQ(pdhkcv.GetDhKeyCheck(), Ea);
 
@@ -265,7 +281,7 @@
 
     .remotely_initiated = true,
     .remote_connection_address = hci::AddressWithType(),
-    .ui_handler = &uiMock,
+    .user_interface = &uiMock,
     .le_security_interface = &leSecurityMock,
     .OnPairingFinished = OnPairingFinished,
 };
@@ -275,6 +291,7 @@
 TEST_F(PairingHandlerUnitTest, test_remote_slave_initiating) {
   initial_informations_trsi.proper_l2cap_interface = up_buffer_.get();
   initial_informations_trsi.l2cap_handler = handler_;
+  initial_informations_trsi.user_interface_handler = handler_;
 
   std::unique_ptr<PairingHandlerLe> pairing_handler =
       std::make_unique<PairingHandlerLe>(PairingHandlerLe::ACCEPT_PROMPT, initial_informations_trsi);
@@ -282,8 +299,9 @@
   // Simulate user accepting the pairing in UI
   pairing_handler->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
 
-  EXPECT_TRUE(WaitForOutgoingL2capPacket());
-  EXPECT_EQ(Code::PAIRING_REQUEST, outgoing_l2cap_packet_->GetCode());
+  std::optional<bluetooth::security::CommandView> pairing_request_pkt = WaitForOutgoingL2capPacket();
+  EXPECT_TRUE(pairing_request_pkt.has_value());
+  EXPECT_EQ(Code::PAIRING_REQUEST, pairing_request_pkt->GetCode());
 
   // We don't care for the rest of the flow, let it die.
   pairing_handler.reset();
@@ -305,7 +323,7 @@
     .pairing_request = PairingRequestView::Create(BuilderToView(
         PairingRequestBuilder::Create(IoCapability::NO_INPUT_NO_OUTPUT, OobDataFlag::NOT_PRESENT,
                                       AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, 16, 0x03, 0x03))),
-    .ui_handler = &uiMock,
+    .user_interface = &uiMock,
     .le_security_interface = &leSecurityMock,
 
     .OnPairingFinished = OnPairingFinished,
@@ -316,6 +334,7 @@
 TEST_F(PairingHandlerUnitTest, test_remote_master_initiating) {
   initial_informations_trmi.proper_l2cap_interface = up_buffer_.get();
   initial_informations_trmi.l2cap_handler = handler_;
+  initial_informations_trmi.user_interface_handler = handler_;
 
   std::unique_ptr<PairingHandlerLe> pairing_handler =
       std::make_unique<PairingHandlerLe>(PairingHandlerLe::ACCEPT_PROMPT, initial_informations_trmi);
@@ -323,8 +342,9 @@
   // Simulate user accepting the pairing in UI
   pairing_handler->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
 
-  EXPECT_TRUE(WaitForOutgoingL2capPacket());
-  EXPECT_EQ(Code::PAIRING_RESPONSE, outgoing_l2cap_packet_->GetCode());
+  std::optional<bluetooth::security::CommandView> pairing_response_pkt = WaitForOutgoingL2capPacket();
+  EXPECT_TRUE(pairing_response_pkt.has_value());
+  EXPECT_EQ(Code::PAIRING_RESPONSE, pairing_response_pkt->GetCode());
   // Phase 1 finished.
 
   // We don't care for the rest of the flow, it's handled in in other tests. let it die.
diff --git a/gd/security/record/security_record.h b/gd/security/record/security_record.h
index 58f047b..287eed9 100644
--- a/gd/security/record/security_record.h
+++ b/gd/security/record/security_record.h
@@ -21,58 +21,118 @@
 #include <memory>
 #include <utility>
 
+#include "crypto_toolbox/crypto_toolbox.h"
 #include "hci/address_with_type.h"
 
 namespace bluetooth {
 namespace security {
 namespace record {
 
-enum BondState { NOT_BONDED, PAIRING, PAIRED, BONDED };
-
 class SecurityRecord {
  public:
-  explicit SecurityRecord(hci::AddressWithType device) : device_(device), state_(NOT_BONDED) {}
+  explicit SecurityRecord(hci::AddressWithType address) : pseudo_address_(address), pairing_(true) {}
 
-  /**
-   * Returns true if the device is bonded to another device
-   */
-  bool IsBonded() {
-    return state_ == BONDED;
-  }
-
-  bool IsPaired() {
-    return state_ == PAIRED;
-  }
+  SecurityRecord& operator=(const SecurityRecord& other) = default;
 
   /**
    * Returns true if a device is currently pairing to another device
    */
-  bool IsPairing() {
-    return state_ == PAIRING;
+  bool IsPairing() const {
+    return pairing_;
+  }
+
+  /* Link key has been exchanged, but not stored */
+  bool IsPaired() const {
+    return IsClassicLinkKeyValid();
+  }
+
+  /**
+   * Returns true if Link Keys are stored persistently
+   */
+  bool IsBonded() const {
+    return IsPaired() && persisted_;
+  }
+
+  /**
+   * Called by storage manager once record has persisted
+   */
+  void SetPersisted(bool persisted) {
+    persisted_ = persisted;
   }
 
   void SetLinkKey(std::array<uint8_t, 16> link_key, hci::KeyType key_type) {
     link_key_ = link_key;
     key_type_ = key_type;
+    CancelPairing();
+  }
+
+  void CancelPairing() {
+    pairing_ = false;
   }
 
   std::array<uint8_t, 16> GetLinkKey() {
+    ASSERT(IsClassicLinkKeyValid());
     return link_key_;
   }
 
   hci::KeyType GetKeyType() {
+    ASSERT(IsClassicLinkKeyValid());
     return key_type_;
   }
 
-  hci::AddressWithType GetDevice() {
-    return device_;
+  hci::AddressWithType GetPseudoAddress() {
+    return pseudo_address_;
+  }
+
+  void SetAuthenticated(bool is_authenticated) {
+    this->is_authenticated_ = is_authenticated;
+  }
+
+  bool IsAuthenticated() {
+    return this->is_authenticated_;
+  }
+
+  void SetRequiresMitmProtection(bool requires_mitm_protection) {
+    this->requires_mitm_protection_ = requires_mitm_protection;
+  }
+
+  bool RequiresMitmProtection() {
+    return this->requires_mitm_protection_;
+  }
+
+  void SetIsEncryptionRequired(bool is_encryption_required) {
+    this->is_encryption_required_ = is_encryption_required;
+  }
+
+  bool IsEncryptionRequired() {
+    return this->is_encryption_required_;
   }
 
  private:
-  const hci::AddressWithType device_;
-  BondState state_;
+  /* First address we have ever seen this device with, that we used to create bond */
+  hci::AddressWithType pseudo_address_;
+
   std::array<uint8_t, 16> link_key_ = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   hci::KeyType key_type_ = hci::KeyType::DEBUG_COMBINATION;
+
+  bool IsClassicLinkKeyValid() const {
+    return !std::all_of(link_key_.begin(), link_key_.end(), [](uint8_t b) { return b == 0; });
+  }
+  bool persisted_ = false;
+  bool pairing_ = false;
+  bool is_authenticated_ = false;
+  bool requires_mitm_protection_ = false;
+  bool is_encryption_required_ = false;
+
+ public:
+  /* Identity Address */
+  std::optional<hci::AddressWithType> identity_address_;
+
+  std::optional<crypto_toolbox::Octet16> ltk;
+  std::optional<uint16_t> ediv;
+  std::optional<std::array<uint8_t, 8>> rand;
+  std::optional<crypto_toolbox::Octet16> irk;
+  std::optional<crypto_toolbox::Octet16> signature_key;
 };
 
 }  // namespace record
diff --git a/gd/security/record/security_record_database.h b/gd/security/record/security_record_database.h
new file mode 100644
index 0000000..63db7e4
--- /dev/null
+++ b/gd/security/record/security_record_database.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <set>
+
+#include "hci/address_with_type.h"
+#include "security/record/security_record.h"
+
+namespace bluetooth {
+namespace security {
+namespace record {
+
+class SecurityRecordDatabase {
+ public:
+  using iterator = std::set<std::shared_ptr<SecurityRecord>>::iterator;
+
+  std::shared_ptr<SecurityRecord> FindOrCreate(hci::AddressWithType address) {
+    auto it = Find(address);
+    // Security record check
+    if (it != records_.end()) return *it;
+
+    // No security record, create one
+    auto record_ptr = std::make_shared<SecurityRecord>(address);
+    records_.insert(record_ptr);
+    return record_ptr;
+  }
+
+  void Remove(const hci::AddressWithType& address) {
+    auto it = Find(address);
+
+    // No record exists
+    if (it == records_.end()) return;
+
+    records_.erase(it);
+  }
+
+  iterator Find(hci::AddressWithType address) {
+    for (auto it = records_.begin(); it != records_.end(); ++it) {
+      std::shared_ptr<SecurityRecord> record = *it;
+      if (record->identity_address_.has_value() && record->identity_address_.value() == address) return it;
+      if (record->GetPseudoAddress() == address) return it;
+      if (record->irk.has_value() && address.IsRpaThatMatchesIrk(record->irk.value())) return it;
+    }
+    return records_.end();
+  }
+
+  std::set<std::shared_ptr<SecurityRecord>> records_;
+};
+
+}  // namespace record
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/security_manager.cc b/gd/security/security_manager.cc
index f9c1440..24e6d0e 100644
--- a/gd/security/security_manager.cc
+++ b/gd/security/security_manager.cc
@@ -19,7 +19,8 @@
 
 #include "os/log.h"
 
-using namespace bluetooth::security;
+namespace bluetooth {
+namespace security {
 
 // Definition of Pure Virtual Destructor
 ISecurityManagerListener::~ISecurityManagerListener() {}
@@ -35,6 +36,12 @@
                                            std::forward<hci::AddressWithType>(device)));
 }
 
+void SecurityManager::CreateBondLe(hci::AddressWithType device) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::CreateBondLe,
+                                           common::Unretained(security_manager_impl_),
+                                           std::forward<hci::AddressWithType>(device)));
+}
+
 void SecurityManager::CancelBond(hci::AddressWithType device) {
   security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::CancelBond,
                                            common::Unretained(security_manager_impl_),
@@ -47,6 +54,27 @@
                                            std::forward<hci::AddressWithType>(device)));
 }
 
+void SecurityManager::SetUserInterfaceHandler(UI* user_interface, os::Handler* handler) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::SetUserInterfaceHandler,
+                                           common::Unretained(security_manager_impl_), user_interface, handler));
+}
+
+void SecurityManager::SetLeInitiatorAddressPolicy(
+    hci::LeAddressManager::AddressPolicy address_policy,
+    hci::AddressWithType fixed_address,
+    crypto_toolbox::Octet16 rotation_irk,
+    std::chrono::milliseconds minimum_rotation_time,
+    std::chrono::milliseconds maximum_rotation_time) {
+  security_handler_->Post(common::BindOnce(
+      &internal::SecurityManagerImpl::SetLeInitiatorAddressPolicy,
+      common::Unretained(security_manager_impl_),
+      address_policy,
+      fixed_address,
+      rotation_irk,
+      minimum_rotation_time,
+      maximum_rotation_time));
+}
+
 void SecurityManager::RegisterCallbackListener(ISecurityManagerListener* listener, os::Handler* handler) {
   security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::RegisterCallbackListener,
                                            common::Unretained(security_manager_impl_), listener, handler));
@@ -56,3 +84,19 @@
   security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::UnregisterCallbackListener,
                                            common::Unretained(security_manager_impl_), listener));
 }
+
+void SecurityManager::OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::OnPairingPromptAccepted,
+                                           common::Unretained(security_manager_impl_), address, confirmed));
+}
+void SecurityManager::OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::OnConfirmYesNo,
+                                           common::Unretained(security_manager_impl_), address, confirmed));
+}
+void SecurityManager::OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::OnPasskeyEntry,
+                                           common::Unretained(security_manager_impl_), address, passkey));
+}
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/security_manager.h b/gd/security/security_manager.h
index 2a9f2e3..ec6eb5b 100644
--- a/gd/security/security_manager.h
+++ b/gd/security/security_manager.h
@@ -22,45 +22,18 @@
 #include <vector>
 
 #include "hci/address_with_type.h"
+#include "hci/le_address_manager.h"
 #include "security/internal/security_manager_impl.h"
+#include "security/security_manager_listener.h"
 
 namespace bluetooth {
 namespace security {
 
 /**
- * Callback interface from SecurityManager.
- */
-class ISecurityManagerListener {
- public:
-  virtual ~ISecurityManagerListener() = 0;
-
-  /**
-   * Called when a device is successfully bonded.
-   *
-   * @param address of the newly bonded device
-   */
-  virtual void OnDeviceBonded(bluetooth::hci::AddressWithType device) = 0;
-
-  /**
-   * Called when a device is successfully un-bonded.
-   *
-   * @param address of device that is no longer bonded
-   */
-  virtual void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) = 0;
-
-  /**
-   * Called as a result of a failure during the bonding process.
-   *
-   * @param address of the device that failed to bond
-   */
-  virtual void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) = 0;
-};
-
-/**
  * Manages the security attributes, pairing, bonding of devices, and the
  * encryption/decryption of communications.
  */
-class SecurityManager {
+class SecurityManager : public UICallbacks {
  public:
   friend class SecurityModule;
 
@@ -70,11 +43,18 @@
   void Init();
 
   /**
-   * Checks the device for existing bond, if not bonded, initiates pairing.
+   * Initiates bond over Classic transport with device, if not bonded yet.
    *
-   * @param device pointer to device we want to bond with
+   * @param address device address we want to bond with
    */
-  void CreateBond(hci::AddressWithType device);
+  void CreateBond(hci::AddressWithType address);
+
+  /**
+   * Initiates bond over Low Energy transport with device, if not bonded yet.
+   *
+   * @param address device address we want to bond with
+   */
+  void CreateBondLe(hci::AddressWithType address);
 
   /**
    * Cancels the pairing process for this device.
@@ -91,6 +71,21 @@
   void RemoveBond(hci::AddressWithType device);
 
   /**
+   * Register Security UI handler, for handling prompts around the Pairing process.
+   */
+  void SetUserInterfaceHandler(UI* user_interface, os::Handler* handler);
+
+  /**
+   * Specify the initiator address policy used for LE transport. Can only be called once.
+   */
+  void SetLeInitiatorAddressPolicy(
+      hci::LeAddressManager::AddressPolicy address_policy,
+      hci::AddressWithType fixed_address,
+      crypto_toolbox::Octet16 rotation_irk,
+      std::chrono::milliseconds minimum_rotation_time,
+      std::chrono::milliseconds maximum_rotation_time);
+
+  /**
    * Register to listen for callback events from SecurityManager
    *
    * @param listener ISecurityManagerListener instance to handle callbacks
@@ -104,6 +99,10 @@
    */
   void UnregisterCallbackListener(ISecurityManagerListener* listener);
 
+  void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) override;
+
  protected:
   SecurityManager(os::Handler* security_handler, internal::SecurityManagerImpl* security_manager_impl)
       : security_handler_(security_handler), security_manager_impl_(security_manager_impl) {}
diff --git a/gd/security/security_manager_listener.h b/gd/security/security_manager_listener.h
new file mode 100644
index 0000000..9cad4aa
--- /dev/null
+++ b/gd/security/security_manager_listener.h
@@ -0,0 +1,64 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "hci/acl_manager.h"
+
+namespace bluetooth {
+namespace security {
+
+/**
+ * Callback interface from SecurityManager.
+ */
+class ISecurityManagerListener {
+ public:
+  virtual ~ISecurityManagerListener() = 0;
+
+  /**
+   * Called when a device is successfully bonded.
+   *
+   * @param address of the newly bonded device
+   */
+  virtual void OnDeviceBonded(bluetooth::hci::AddressWithType device) = 0;
+
+  /**
+   * Called when a device is successfully un-bonded.
+   *
+   * @param address of device that is no longer bonded
+   */
+  virtual void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) = 0;
+
+  /**
+   * Called as a result of a failure during the bonding process.
+   *
+   * @param address of the device that failed to bond
+   */
+  virtual void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) = 0;
+
+  /**
+   * Called as a result of a failure during the bonding process.
+   *
+   * @param address of the device that failed to bond
+   */
+  virtual void OnEncryptionStateChanged(hci::EncryptionChangeView encryption_change_view) = 0;
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/security_module.cc b/gd/security/security_module.cc
index eb0a543..8d4332f 100644
--- a/gd/security/security_module.cc
+++ b/gd/security/security_module.cc
@@ -21,10 +21,13 @@
 #include "os/handler.h"
 #include "os/log.h"
 
+#include "hci/acl_manager.h"
 #include "hci/hci_layer.h"
 #include "l2cap/le/l2cap_le_module.h"
 #include "security/channel/security_manager_channel.h"
+#include "security/facade_configuration_api.h"
 #include "security/internal/security_manager_impl.h"
+#include "security/l2cap_security_module_interface.h"
 #include "security/security_module.h"
 
 namespace bluetooth {
@@ -33,22 +36,40 @@
 const ModuleFactory SecurityModule::Factory = ModuleFactory([]() { return new SecurityModule(); });
 
 struct SecurityModule::impl {
-  impl(os::Handler* security_handler, l2cap::le::L2capLeModule* l2cap_le_module,
-       l2cap::classic::L2capClassicModule* l2cap_classic_module, hci::HciLayer* hci_layer)
-      : security_handler_(security_handler), l2cap_le_module_(l2cap_le_module),
+  impl(
+      os::Handler* security_handler,
+      l2cap::le::L2capLeModule* l2cap_le_module,
+      l2cap::classic::L2capClassicModule* l2cap_classic_module,
+      hci::HciLayer* hci_layer,
+      hci::AclManager* acl_manager)
+      : security_handler_(security_handler),
         l2cap_classic_module_(l2cap_classic_module),
+        l2cap_le_module_(l2cap_le_module),
         security_manager_channel_(new channel::SecurityManagerChannel(security_handler_, hci_layer)),
-        hci_layer_(hci_layer) {}
+        hci_layer_(hci_layer),
+        acl_manager_(acl_manager),
+        l2cap_security_interface_(&security_manager_impl, security_handler) {
+    l2cap_classic_module->InjectSecurityEnforcementInterface(&l2cap_security_interface_);
+    l2cap_le_module->InjectSecurityEnforcementInterface(&l2cap_security_interface_);
+    security_manager_channel_->SetSecurityInterface(
+        l2cap_classic_module->GetSecurityInterface(security_handler_, security_manager_channel_));
+  }
 
   os::Handler* security_handler_;
-  l2cap::le::L2capLeModule* l2cap_le_module_;
   l2cap::classic::L2capClassicModule* l2cap_classic_module_;
+  l2cap::le::L2capLeModule* l2cap_le_module_;
   channel::SecurityManagerChannel* security_manager_channel_;
   hci::HciLayer* hci_layer_;
-  internal::SecurityManagerImpl security_manager_impl{security_handler_, l2cap_le_module_, l2cap_classic_module_,
-                                                      security_manager_channel_, hci_layer_};
+  hci::AclManager* acl_manager_;
+  L2capSecurityModuleInterface l2cap_security_interface_;
+
+  internal::SecurityManagerImpl security_manager_impl{
+      security_handler_, l2cap_le_module_, security_manager_channel_, hci_layer_, acl_manager_};
+
   ~impl() {
     delete security_manager_channel_;
+    l2cap_classic_module_->InjectSecurityEnforcementInterface(nullptr);
+    l2cap_le_module_->InjectSecurityEnforcementInterface(nullptr);
   }
 };
 
@@ -56,11 +77,15 @@
   list->add<l2cap::le::L2capLeModule>();
   list->add<l2cap::classic::L2capClassicModule>();
   list->add<hci::HciLayer>();
+  list->add<hci::AclManager>();
 }
 
 void SecurityModule::Start() {
   pimpl_ = std::make_unique<impl>(GetHandler(), GetDependency<l2cap::le::L2capLeModule>(),
-                                  GetDependency<l2cap::classic::L2capClassicModule>(), GetDependency<hci::HciLayer>());
+                                  GetDependency<l2cap::classic::L2capClassicModule>(), GetDependency<hci::HciLayer>(),
+                                  GetDependency<hci::AclManager>());
+
+  GetDependency<hci::AclManager>()->SetSecurityModule(this);
 }
 
 void SecurityModule::Stop() {
@@ -76,5 +101,10 @@
       new SecurityManager(pimpl_->security_handler_, &pimpl_->security_manager_impl));
 }
 
+std::unique_ptr<FacadeConfigurationApi> SecurityModule::GetFacadeConfigurationApi() {
+  return std::unique_ptr<FacadeConfigurationApi>(
+      new FacadeConfigurationApi(pimpl_->security_handler_, &pimpl_->security_manager_impl));
+}
+
 }  // namespace security
 }  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/security_module.h b/gd/security/security_module.h
index 2ec456d..18f2543 100644
--- a/gd/security/security_module.h
+++ b/gd/security/security_module.h
@@ -18,6 +18,7 @@
 #include <memory>
 
 #include "module.h"
+#include "security/facade_configuration_api.h"
 #include "security/security_manager.h"
 
 namespace bluetooth {
@@ -33,6 +34,13 @@
    */
   std::unique_ptr<SecurityManager> GetSecurityManager();
 
+  /**
+   * Facade configuration API.
+   *
+   * <p> This allows you to set thins like IO Capabilities, Authentication Requirements, and OOB Data.
+   */
+  std::unique_ptr<FacadeConfigurationApi> GetFacadeConfigurationApi();
+
   static const ModuleFactory Factory;
 
  protected:
diff --git a/gd/security/test/ecdh_keys_test.cc b/gd/security/test/ecdh_keys_test.cc
new file mode 100644
index 0000000..89e7dd6
--- /dev/null
+++ b/gd/security/test/ecdh_keys_test.cc
@@ -0,0 +1,113 @@
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include <base/strings/string_number_conversions.h>
+#include "hci/le_security_interface.h"
+#include "os/log.h"
+#include "security/ecc/p_256_ecc_pp.h"
+#include "security/ecdh_keys.h"
+#include "security/test/mocks.h"
+
+using namespace std::chrono_literals;
+
+namespace bluetooth {
+namespace security {
+
+class EcdhKeysTest : public testing::Test {
+ protected:
+  void SetUp() {}
+
+  void TearDown() {}
+
+ public:
+};
+
+/* This test generates two pairs of keys, computes the Diffie–Hellman key using both, and verifies that they match */
+TEST_F(EcdhKeysTest, test_generated) {
+  std::srand(std::time(nullptr));
+
+  auto [private_key_a, public_key_a] = GenerateECDHKeyPair();
+  auto [private_key_b, public_key_b] = GenerateECDHKeyPair();
+
+  std::array<uint8_t, 32> dhkeya = ComputeDHKey(private_key_a, public_key_b);
+  std::array<uint8_t, 32> dhkeyb = ComputeDHKey(private_key_b, public_key_a);
+
+  EXPECT_EQ(dhkeya, dhkeyb);
+
+  if (dhkeya != dhkeyb) {
+    LOG_ERROR("private key a : %s", base::HexEncode(private_key_a.data(), private_key_a.size()).c_str());
+    LOG_ERROR("public key a.x : %s", base::HexEncode(public_key_a.x.data(), public_key_a.x.size()).c_str());
+    LOG_ERROR("public key a.y : %s", base::HexEncode(public_key_a.y.data(), public_key_a.y.size()).c_str());
+
+    LOG_ERROR("private key b : %s", base::HexEncode(private_key_b.data(), private_key_b.size()).c_str());
+    LOG_ERROR("public key b.x : %s", base::HexEncode(public_key_b.x.data(), public_key_b.x.size()).c_str());
+    LOG_ERROR("public key b.y : %s", base::HexEncode(public_key_b.y.data(), public_key_b.y.size()).c_str());
+
+    LOG_ERROR("dhkeya : %s", base::HexEncode(dhkeya.data(), dhkeya.size()).c_str());
+    LOG_ERROR("dhkeyb : %s", base::HexEncode(dhkeyb.data(), dhkeyb.size()).c_str());
+  }
+}
+
+/* This test uses two fixed pairs of keys, computes the Diffie–Hellman key using both, and verifies that they match
+precomputed value.
+
+This code is also useful during debuging - one can replace fixed values with values from failed exchagne to verify which
+side did bad computation. */
+TEST_F(EcdhKeysTest, test_static) {
+  std::array<uint8_t, 32> private_key_a = {0x3E, 0xC8, 0x2A, 0x32, 0xB3, 0x75, 0x76, 0xBA, 0x7D, 0xB8, 0xB4,
+                                           0x7B, 0xA0, 0x8A, 0xA3, 0xC3, 0xF2, 0x03, 0x1A, 0x53, 0xF6, 0x52,
+                                           0x26, 0x32, 0xB6, 0xAE, 0x57, 0x3F, 0x13, 0x15, 0x29, 0x51};
+  bluetooth::security::EcdhPublicKey public_key_a;
+  uint8_t ax[32] = {0xDC, 0x88, 0xD0, 0xE5, 0x59, 0x73, 0xF2, 0x41, 0x88, 0x6C, 0xB4, 0x45, 0x8B, 0x61, 0x3B, 0x10,
+                    0xF5, 0xD4, 0xD2, 0x5B, 0x4E, 0xA1, 0x7F, 0x94, 0xE3, 0xA9, 0x38, 0xF8, 0x84, 0xD4, 0x98, 0x10};
+  uint8_t ay[32] = {0x3D, 0x13, 0x76, 0x4F, 0xD1, 0x29, 0x6E, 0xEC, 0x8D, 0xF6, 0x70, 0x33, 0x8B, 0xA7, 0x18, 0xEA,
+                    0x84, 0x15, 0xE8, 0x8C, 0x4A, 0xC8, 0x76, 0x45, 0x90, 0x98, 0xBA, 0x52, 0x8B, 0x00, 0x69, 0xAF};
+  memcpy(public_key_a.x.data(), ax, 32);
+  memcpy(public_key_a.y.data(), ay, 32);
+
+  std::array<uint8_t, 32> private_key_b = {0xDD, 0x53, 0x84, 0x91, 0xC8, 0xFA, 0x4B, 0x45, 0xB2, 0xFF, 0xC0,
+                                           0x53, 0x89, 0x64, 0x16, 0x7B, 0x67, 0x30, 0xCE, 0x5D, 0x82, 0xF4,
+                                           0x8F, 0x38, 0xA2, 0xE6, 0x78, 0xB6, 0xFB, 0xA1, 0x07, 0xD8};
+  bluetooth::security::EcdhPublicKey public_key_b;
+  uint8_t bx[32] = {0x23, 0x1A, 0xEC, 0xFE, 0x7D, 0xC1, 0x20, 0x2F, 0x03, 0x3E, 0x9A, 0xAA, 0x99, 0x55, 0x78, 0x86,
+                    0x58, 0xCB, 0x37, 0x68, 0x7D, 0xE1, 0xFF, 0x19, 0x33, 0xF8, 0xCB, 0x7A, 0x17, 0xAB, 0x0B, 0x73};
+  uint8_t by[32] = {0x4C, 0x25, 0xE2, 0x42, 0x3C, 0x69, 0x0E, 0x3B, 0xC0, 0xEF, 0x94, 0x09, 0x4D, 0x3F, 0x96, 0xBB,
+                    0x18, 0xF2, 0x55, 0x81, 0x71, 0x5A, 0xDE, 0xC4, 0x3E, 0xF9, 0x6F, 0xA9, 0xAF, 0x04, 0x4E, 0x86};
+  memcpy(public_key_b.x.data(), bx, 32);
+  memcpy(public_key_b.y.data(), by, 32);
+
+  std::array<uint8_t, 32> dhkey;
+  uint8_t dhkey_val[32] = {0x3B, 0xF8, 0xDF, 0x33, 0x99, 0x94, 0x66, 0x55, 0x4F, 0x2C, 0x4A,
+                           0x78, 0x2B, 0x51, 0xD1, 0x49, 0x0F, 0xF1, 0x96, 0x63, 0x51, 0x75,
+                           0x9E, 0x65, 0x7F, 0x3C, 0xFE, 0x77, 0xB4, 0x3F, 0x7A, 0x93};
+  memcpy(dhkey.data(), dhkey_val, 32);
+
+  std::array<uint8_t, 32> dhkey_a = ComputeDHKey(private_key_a, public_key_b);
+  std::array<uint8_t, 32> dhkey_b = ComputeDHKey(private_key_b, public_key_a);
+
+  EXPECT_EQ(dhkey_a, dhkey);
+  EXPECT_EQ(dhkey_b, dhkey);
+}
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/test/fake_hci_layer.h b/gd/security/test/fake_hci_layer.h
index 6866b34..0104546 100644
--- a/gd/security/test/fake_hci_layer.h
+++ b/gd/security/test/fake_hci_layer.h
@@ -22,7 +22,7 @@
 namespace bluetooth {
 namespace security {
 
-using common::OnceCallback;
+using common::ContextualOnceCallback;
 using hci::CommandCompleteView;
 using hci::CommandPacketBuilder;
 using hci::CommandStatusView;
@@ -30,7 +30,6 @@
 using hci::EventPacketBuilder;
 using hci::EventPacketView;
 using hci::HciLayer;
-using os::Handler;
 
 namespace {
 
@@ -45,35 +44,32 @@
 class CommandQueueEntry {
  public:
   CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
-                    OnceCallback<void(CommandCompleteView)> on_complete_function, Handler* handler)
-      : command(std::move(command_packet)), waiting_for_status_(false), on_complete(std::move(on_complete_function)),
-        caller_handler(handler) {}
+                    ContextualOnceCallback<void(CommandCompleteView)> on_complete_function)
+      : command(std::move(command_packet)), waiting_for_status_(false), on_complete(std::move(on_complete_function)) {}
 
   CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
-                    OnceCallback<void(CommandStatusView)> on_status_function, Handler* handler)
-      : command(std::move(command_packet)), waiting_for_status_(true), on_status(std::move(on_status_function)),
-        caller_handler(handler) {}
+                    ContextualOnceCallback<void(CommandStatusView)> on_status_function)
+      : command(std::move(command_packet)), waiting_for_status_(true), on_status(std::move(on_status_function)) {}
 
   std::unique_ptr<CommandPacketBuilder> command;
   bool waiting_for_status_;
-  OnceCallback<void(CommandStatusView)> on_status;
-  OnceCallback<void(CommandCompleteView)> on_complete;
-  Handler* caller_handler;
+  ContextualOnceCallback<void(CommandStatusView)> on_status;
+  ContextualOnceCallback<void(CommandCompleteView)> on_complete;
 };
 
 }  // namespace
 
 class FakeHciLayer : public HciLayer {
  public:
-  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status,
-                      Handler* handler) override {
-    auto command_queue_entry = std::make_unique<CommandQueueEntry>(std::move(command), std::move(on_status), handler);
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      ContextualOnceCallback<void(CommandStatusView)> on_status) override {
+    auto command_queue_entry = std::make_unique<CommandQueueEntry>(std::move(command), std::move(on_status));
     command_queue_.push(std::move(command_queue_entry));
   }
 
   void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
-                      OnceCallback<void(CommandCompleteView)> on_complete, Handler* handler) override {
-    auto command_queue_entry = std::make_unique<CommandQueueEntry>(std::move(command), std::move(on_complete), handler);
+                      ContextualOnceCallback<void(CommandCompleteView)> on_complete) override {
+    auto command_queue_entry = std::make_unique<CommandQueueEntry>(std::move(command), std::move(on_complete));
     command_queue_.push(std::move(command_queue_entry));
   }
 
@@ -84,8 +80,8 @@
     return last;
   }
 
-  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
-                            Handler* handler) override {
+  void RegisterEventHandler(EventCode event_code,
+                            common::ContextualCallback<void(EventPacketView)> event_handler) override {
     registered_events_[event_code] = event_handler;
   }
 
@@ -99,7 +95,7 @@
     ASSERT_TRUE(event.IsValid());
     EventCode event_code = event.GetEventCode();
     ASSERT_TRUE(registered_events_.find(event_code) != registered_events_.end());
-    registered_events_[event_code].Run(event);
+    registered_events_[event_code].Invoke(event);
   }
 
   void ListDependencies(ModuleList* list) override {}
@@ -107,7 +103,7 @@
   void Stop() override {}
 
  private:
-  std::map<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
+  std::map<EventCode, common::ContextualCallback<void(EventPacketView)>> registered_events_;
   std::queue<std::unique_ptr<CommandQueueEntry>> command_queue_;
 };
 
diff --git a/gd/security/test/fake_security_interface.h b/gd/security/test/fake_security_interface.h
new file mode 100644
index 0000000..9ff3845
--- /dev/null
+++ b/gd/security/test/fake_security_interface.h
@@ -0,0 +1,63 @@
+/*
+ *
+ *  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.
+ *
+ */
+#include "common/bind.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace security {
+
+class FakeLinkSecurityInterface : public l2cap::classic::LinkSecurityInterface {
+ public:
+  FakeLinkSecurityInterface(l2cap::classic::LinkSecurityInterfaceListener* listener, hci::Address address)
+      : listener_(listener), address_(address) {}
+
+  hci::Address GetRemoteAddress() {
+    return address_;
+  }
+  void Hold() override {}
+  void EnsureAuthenticated() override{};
+  void Release() override {
+    // TODO(optedoblivion): Simulate the delay
+    listener_->OnLinkDisconnected(address_);
+  }
+  void Disconnect() override {
+    listener_->OnLinkDisconnected(address_);
+  }
+
+ private:
+  l2cap::classic::LinkSecurityInterfaceListener* listener_ = nullptr;
+  hci::Address address_;
+};
+
+class FakeSecurityInterface : public l2cap::classic::SecurityInterface {
+ public:
+  FakeSecurityInterface(os::Handler* handler, l2cap::classic::LinkSecurityInterfaceListener* listener)
+      : handler_(handler), listener_(listener) {}
+  ~FakeSecurityInterface() {}
+  void InitiateConnectionForSecurity(hci::Address remote) override {
+    listener_->OnLinkConnected(std::make_unique<FakeLinkSecurityInterface>(listener_, remote));
+  };
+  void Unregister() override {}
+
+ private:
+  os::Handler* handler_ __attribute__((unused));
+  l2cap::classic::LinkSecurityInterfaceListener* listener_ __attribute__((unused));
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/test/mocks.h b/gd/security/test/mocks.h
index 812c9a1..1c1ec90 100644
--- a/gd/security/test/mocks.h
+++ b/gd/security/test/mocks.h
@@ -32,11 +32,12 @@
   UIMock() {}
   ~UIMock() override = default;
 
-  MOCK_METHOD2(DisplayPairingPrompt, void(const bluetooth::hci::AddressWithType&, std::string&));
-  MOCK_METHOD1(CancelPairingPrompt, void(const bluetooth::hci::AddressWithType&));
-  MOCK_METHOD1(DisplayConfirmValue, void(uint32_t));
-  MOCK_METHOD0(DisplayEnterPasskeyDialog, void());
-  MOCK_METHOD1(DisplayPasskey, void(uint32_t));
+  MOCK_METHOD2(DisplayPairingPrompt, void(const bluetooth::hci::AddressWithType&, std::string));
+  MOCK_METHOD1(Cancel, void(const bluetooth::hci::AddressWithType&));
+  MOCK_METHOD3(DisplayConfirmValue, void(const bluetooth::hci::AddressWithType&, std::string, uint32_t));
+  MOCK_METHOD2(DisplayYesNoDialog, void(const bluetooth::hci::AddressWithType&, std::string));
+  MOCK_METHOD2(DisplayEnterPasskeyDialog, void(const bluetooth::hci::AddressWithType&, std::string));
+  MOCK_METHOD3(DisplayPasskey, void(const bluetooth::hci::AddressWithType&, std::string, uint32_t));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(UIMock);
@@ -44,13 +45,11 @@
 
 class LeSecurityInterfaceMock : public hci::LeSecurityInterface {
  public:
-  MOCK_METHOD3(EnqueueCommand,
-               void(std::unique_ptr<hci::LeSecurityCommandBuilder> command,
-                    common::OnceCallback<void(hci::CommandCompleteView)> on_complete, os::Handler* handler));
-  MOCK_METHOD3(EnqueueCommand,
-               void(std::unique_ptr<hci::LeSecurityCommandBuilder> command,
-                    common::OnceCallback<void(hci::CommandStatusView)> on_status, os::Handler* handler));
+  MOCK_METHOD2(EnqueueCommand, void(std::unique_ptr<hci::LeSecurityCommandBuilder> command,
+                                    common::ContextualOnceCallback<void(hci::CommandCompleteView)> on_complete));
+  MOCK_METHOD2(EnqueueCommand, void(std::unique_ptr<hci::LeSecurityCommandBuilder> command,
+                                    common::ContextualOnceCallback<void(hci::CommandStatusView)> on_status));
 };
 
 }  // namespace security
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/security/test/pairing_handler_le_pair_test.cc b/gd/security/test/pairing_handler_le_pair_test.cc
index 1bed89c..0f5bfaa 100644
--- a/gd/security/test/pairing_handler_le_pair_test.cc
+++ b/gd/security/test/pairing_handler_le_pair_test.cc
@@ -178,7 +178,8 @@
         .remotely_initiated = false,
         .connection_handle = CONN_HANDLE_MASTER,
         .remote_connection_address = {ADDRESS_SLAVE, ADDRESS_TYPE_SLAVE},
-        .ui_handler = &master_ui_handler,
+        .user_interface = &master_user_interface,
+        .user_interface_handler = handler_,
         .le_security_interface = &master_le_security_mock,
         .proper_l2cap_interface = up_buffer_a_.get(),
         .l2cap_handler = handler_,
@@ -198,7 +199,8 @@
         .remotely_initiated = true,
         .connection_handle = CONN_HANDLE_SLAVE,
         .remote_connection_address = {ADDRESS_MASTER, ADDRESS_TYPE_MASTER},
-        .ui_handler = &slave_ui_handler,
+        .user_interface = &slave_user_interface,
+        .user_interface_handler = handler_,
         .le_security_interface = &slave_le_security_mock,
         .proper_l2cap_interface = up_buffer_b_.get(),
         .l2cap_handler = handler_,
@@ -209,8 +211,8 @@
   }
 
   void TearDown() {
-    ::testing::Mock::VerifyAndClearExpectations(&slave_ui_handler);
-    ::testing::Mock::VerifyAndClearExpectations(&master_ui_handler);
+    ::testing::Mock::VerifyAndClearExpectations(&slave_user_interface);
+    ::testing::Mock::VerifyAndClearExpectations(&master_user_interface);
     ::testing::Mock::VerifyAndClearExpectations(&slave_le_security_mock);
     ::testing::Mock::VerifyAndClearExpectations(&master_le_security_mock);
 
@@ -250,10 +252,10 @@
     // For now, all tests are succeeding to go through Encryption. Record that in the setup.
     //  Once we test failure cases, move this to each test
     EXPECT_CALL(master_le_security_mock,
-                EnqueueCommand(_, Matcher<common::OnceCallback<void(CommandStatusView)>>(_), _))
+                EnqueueCommand(_, Matcher<common::ContextualOnceCallback<void(CommandStatusView)>>(_)))
         .Times(1)
         .WillOnce([](std::unique_ptr<LeSecurityCommandBuilder> command,
-                     common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) {
+                     common::ContextualOnceCallback<void(CommandStatusView)> on_status) {
           // TODO: on_status.Run();
 
           pairing_handler_a->OnHciEvent(EventBuilderToView(
@@ -276,8 +278,8 @@
 
   InitialInformations master_setup;
   InitialInformations slave_setup;
-  UIMock master_ui_handler;
-  UIMock slave_ui_handler;
+  UIMock master_user_interface;
+  UIMock slave_user_interface;
   LeSecurityInterfaceMock master_le_security_mock;
   LeSecurityInterfaceMock slave_le_security_mock;
 
@@ -308,7 +310,7 @@
     auto first_pkt = WaitFirstL2capCommand();
     slave_setup.pairing_request = PairingRequestView::Create(*first_pkt);
 
-    EXPECT_CALL(slave_ui_handler, DisplayPairingPrompt(_, _)).Times(1).WillOnce(InvokeWithoutArgs([] {
+    EXPECT_CALL(slave_user_interface, DisplayPairingPrompt(_, _)).Times(1).WillOnce(InvokeWithoutArgs([] {
       LOG_INFO("UI mock received pairing prompt");
 
       {
@@ -345,7 +347,8 @@
       .remotely_initiated = true,
       .connection_handle = CONN_HANDLE_MASTER,
       .remote_connection_address = {ADDRESS_SLAVE, ADDRESS_TYPE_SLAVE},
-      .ui_handler = &master_ui_handler,
+      .user_interface = &master_user_interface,
+      .user_interface_handler = handler_,
       .le_security_interface = &master_le_security_mock,
       .proper_l2cap_interface = up_buffer_a_.get(),
       .l2cap_handler = handler_,
@@ -364,7 +367,8 @@
       .remotely_initiated = false,
       .connection_handle = CONN_HANDLE_SLAVE,
       .remote_connection_address = {ADDRESS_MASTER, ADDRESS_TYPE_MASTER},
-      .ui_handler = &slave_ui_handler,
+      .user_interface = &slave_user_interface,
+      .user_interface_handler = handler_,
       .le_security_interface = &slave_le_security_mock,
       .proper_l2cap_interface = up_buffer_b_.get(),
       .l2cap_handler = handler_,
@@ -378,22 +382,24 @@
 
     first_pkt = WaitFirstL2capCommand();
 
-    EXPECT_CALL(master_ui_handler, DisplayPairingPrompt(_, _)).Times(1).WillOnce(InvokeWithoutArgs([&first_pkt, this] {
-      LOG_INFO("UI mock received pairing prompt");
+    EXPECT_CALL(master_user_interface, DisplayPairingPrompt(_, _))
+        .Times(1)
+        .WillOnce(InvokeWithoutArgs([&first_pkt, this] {
+          LOG_INFO("UI mock received pairing prompt");
 
-      {
-        // By grabbing the lock, we ensure initialization of both pairing handlers is finished.
-        std::lock_guard<std::mutex> lock(handlers_initialization_guard);
-      }
-      if (!pairing_handler_a) LOG_ALWAYS_FATAL("handler not initalized yet!");
-      // Simulate user accepting the pairing in UI
-      pairing_handler_a->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
+          {
+            // By grabbing the lock, we ensure initialization of both pairing handlers is finished.
+            std::lock_guard<std::mutex> lock(handlers_initialization_guard);
+          }
+          if (!pairing_handler_a) LOG_ALWAYS_FATAL("handler not initalized yet!");
+          // Simulate user accepting the pairing in UI
+          pairing_handler_a->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
 
-      // Send the first packet from the slave to master
-      auto view_to_packet = std::make_unique<packet::RawBuilder>();
-      view_to_packet->AddOctets(std::vector(first_pkt->begin(), first_pkt->end()));
-      up_buffer_b_->Enqueue(std::move(view_to_packet), handler_);
-    }));
+          // Send the first packet from the slave to master
+          auto view_to_packet = std::make_unique<packet::RawBuilder>();
+          view_to_packet->AddOctets(std::vector(first_pkt->begin(), first_pkt->end()));
+          up_buffer_b_->Enqueue(std::move(view_to_packet), handler_);
+        }));
     pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
   }
 
@@ -425,16 +431,17 @@
     }
     slave_setup.pairing_request = PairingRequestView::Create(*first_command);
 
-    RecordPairingPromptHandling(slave_ui_handler, &pairing_handler_b);
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
 
-    EXPECT_CALL(slave_ui_handler, DisplayConfirmValue(_)).WillOnce(SaveArg<0>(&num_value_slave));
-    EXPECT_CALL(master_ui_handler, DisplayConfirmValue(_)).WillOnce(Invoke([&](uint32_t num_value) {
-      EXPECT_EQ(num_value_slave, num_value);
-      if (num_value_slave == num_value) {
-        pairing_handler_a->OnUiAction(PairingEvent::CONFIRM_YESNO, 0x01);
-        pairing_handler_b->OnUiAction(PairingEvent::CONFIRM_YESNO, 0x01);
-      }
-    }));
+    EXPECT_CALL(slave_user_interface, DisplayConfirmValue(_, _, _)).WillOnce(SaveArg<2>(&num_value_slave));
+    EXPECT_CALL(master_user_interface, DisplayConfirmValue(_, _, _))
+        .WillOnce(Invoke([&](const bluetooth::hci::AddressWithType&, std::string, uint32_t num_value) {
+          EXPECT_EQ(num_value_slave, num_value);
+          if (num_value_slave == num_value) {
+            pairing_handler_a->OnUiAction(PairingEvent::CONFIRM_YESNO, 0x01);
+            pairing_handler_b->OnUiAction(PairingEvent::CONFIRM_YESNO, 0x01);
+          }
+        }));
 
     pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
   }
@@ -454,7 +461,10 @@
   slave_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
   slave_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc;
 
-  uint32_t passkey = std::numeric_limits<uint32_t>::max();
+  // In this test either master or slave display the UI prompt first. This variable makes sure both prompts are
+  // displayed before passkey is confirmed. Since both UI handlers are same thread, it's safe.
+  int ui_prompts_count = 0;
+  uint32_t passkey_ = std::numeric_limits<uint32_t>::max();
   {
     std::unique_lock<std::mutex> lock(handlers_initialization_guard);
     pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
@@ -465,18 +475,24 @@
     }
     slave_setup.pairing_request = PairingRequestView::Create(*first_command);
 
-    RecordPairingPromptHandling(slave_ui_handler, &pairing_handler_b);
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
 
-    EXPECT_CALL(slave_ui_handler, DisplayPasskey(_)).WillOnce(SaveArg<0>(&passkey));
-    EXPECT_CALL(master_ui_handler, DisplayEnterPasskeyDialog()).WillOnce(Invoke([&]() {
-      LOG_INFO("Passkey prompt displayed entering passkey: %08x", passkey);
-      std::this_thread::sleep_for(1ms);
+    EXPECT_CALL(slave_user_interface, DisplayPasskey(_, _, _))
+        .WillOnce(Invoke([&](const bluetooth::hci::AddressWithType& address, std::string name, uint32_t passkey) {
+          passkey_ = passkey;
+          ui_prompts_count++;
+          if (ui_prompts_count == 2) {
+            pairing_handler_a->OnUiAction(PairingEvent::PASSKEY, passkey);
+          }
+        }));
 
-      // handle case where prompts are displayed in different order in the test!
-      if (passkey == std::numeric_limits<uint32_t>::max()) FAIL();
-
-      pairing_handler_a->OnUiAction(PairingEvent::PASSKEY, passkey);
-    }));
+    EXPECT_CALL(master_user_interface, DisplayEnterPasskeyDialog(_, _))
+        .WillOnce(Invoke([&](const bluetooth::hci::AddressWithType& address, std::string name) {
+          ui_prompts_count++;
+          if (ui_prompts_count == 2) {
+            pairing_handler_a->OnUiAction(PairingEvent::PASSKEY, passkey_);
+          }
+        }));
 
     pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
   }
@@ -513,7 +529,7 @@
     }
     slave_setup.pairing_request = PairingRequestView::Create(*first_command);
 
-    RecordPairingPromptHandling(slave_ui_handler, &pairing_handler_b);
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
 
     pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
   }
@@ -556,7 +572,7 @@
     }
     slave_setup.pairing_request = PairingRequestView::Create(*first_command);
 
-    RecordPairingPromptHandling(slave_ui_handler, &pairing_handler_b);
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
 
     pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
   }
@@ -585,7 +601,7 @@
     }
     slave_setup.pairing_request = PairingRequestView::Create(*first_command);
 
-    RecordPairingPromptHandling(slave_ui_handler, &pairing_handler_b);
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
 
     pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
   }
@@ -614,16 +630,17 @@
     }
     slave_setup.pairing_request = PairingRequestView::Create(*first_command);
 
-    RecordPairingPromptHandling(slave_ui_handler, &pairing_handler_b);
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
 
-    EXPECT_CALL(slave_ui_handler, DisplayEnterPasskeyDialog());
-    EXPECT_CALL(master_ui_handler, DisplayConfirmValue(_)).WillOnce(Invoke([&](uint32_t passkey) {
-      LOG_INFO("Passkey prompt displayed entering passkey: %08x", passkey);
-      std::this_thread::sleep_for(1ms);
+    EXPECT_CALL(slave_user_interface, DisplayEnterPasskeyDialog(_, _));
+    EXPECT_CALL(master_user_interface, DisplayConfirmValue(_, _, _))
+        .WillOnce(Invoke([&](const bluetooth::hci::AddressWithType&, std::string, uint32_t passkey) {
+          LOG_INFO("Passkey prompt displayed entering passkey: %08x", passkey);
+          std::this_thread::sleep_for(1ms);
 
-      // TODO: handle case where prompts are displayed in different order in the test!
-      pairing_handler_b->OnUiAction(PairingEvent::PASSKEY, passkey);
-    }));
+          // TODO: handle case where prompts are displayed in different order in the test!
+          pairing_handler_b->OnUiAction(PairingEvent::PASSKEY, passkey);
+        }));
 
     pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
   }
diff --git a/gd/security/ui.h b/gd/security/ui.h
index abb943e..ceecd14 100644
--- a/gd/security/ui.h
+++ b/gd/security/ui.h
@@ -28,36 +28,40 @@
  public:
   virtual ~UI(){};
 
-  /* Remote device tries to initiate pairing, ask user to confirm */
-  virtual void DisplayPairingPrompt(const bluetooth::hci::AddressWithType& address, std::string& name) = 0;
+  /* Remote LE device tries to initiate pairing, ask user to confirm */
+  virtual void DisplayPairingPrompt(const bluetooth::hci::AddressWithType& address, std::string name) = 0;
 
   /* Remove the pairing prompt from DisplayPairingPrompt, i.e. remote device disconnected, or some application requested
    * bond with this device */
-  virtual void CancelPairingPrompt(const bluetooth::hci::AddressWithType& address) = 0;
+  virtual void Cancel(const bluetooth::hci::AddressWithType& address) = 0;
 
-  /* Display value for Comprision */
-  virtual void DisplayConfirmValue(uint32_t numeric_value) = 0;
+  /* Display value for Comprision, user responds yes/no */
+  virtual void DisplayConfirmValue(const bluetooth::hci::AddressWithType& address, std::string name,
+                                   uint32_t numeric_value) = 0;
+
+  /* Display Yes/No dialog, Classic pairing, numeric comparison with NoInputNoOutput device */
+  virtual void DisplayYesNoDialog(const bluetooth::hci::AddressWithType& address, std::string name) = 0;
 
   /* Display a dialog box that will let user enter the Passkey */
-  virtual void DisplayEnterPasskeyDialog() = 0;
+  virtual void DisplayEnterPasskeyDialog(const bluetooth::hci::AddressWithType& address, std::string name) = 0;
 
-  /* Present the passkey value to the user */
-  virtual void DisplayPasskey(uint32_t passkey) = 0;
+  /* Present the passkey value to the user, user compares with other device */
+  virtual void DisplayPasskey(const bluetooth::hci::AddressWithType& address, std::string name, uint32_t passkey) = 0;
 };
 
 /* Through this interface, UI provides us with user choices. */
 class UICallbacks {
  public:
-  virtual ~UICallbacks() = 0;
+  virtual ~UICallbacks() = default;
 
   /* User accepted pairing prompt */
-  virtual void OnPairingPromptAccepted(const bluetooth::hci::Address& address) = 0;
+  virtual void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) = 0;
 
   /* User confirmed that displayed value matches the value on the other device */
-  virtual void OnConfirmYesNo(const bluetooth::hci::Address& address, bool conformed) = 0;
+  virtual void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) = 0;
 
   /* User typed the value displayed on the other device. This is either Passkey or the Confirm value */
-  virtual void OnPasskeyEntry(const bluetooth::hci::Address& address, uint32_t passkey) = 0;
+  virtual void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) = 0;
 };
 
 }  // namespace security
diff --git a/gd/setup.py b/gd/setup.py
new file mode 100644
index 0000000..e794804
--- /dev/null
+++ b/gd/setup.py
@@ -0,0 +1,112 @@
+#!/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 distutils import log
+from distutils.errors import DistutilsModuleError
+import os
+from setuptools import find_packages
+from setuptools import setup
+from setuptools.command.install import install
+import stat
+import subprocess
+import sys
+
+install_requires = [
+    'grpcio',
+    'psutil',
+]
+
+host_executables = [
+    'root-canal',
+    'bluetooth_stack_with_facade',
+]
+
+
+# Need to verify acts is importable in a new Python context
+def is_acts_importable():
+    cmd = [sys.executable, '-c', 'import acts']
+    completed_process = subprocess.run(cmd, cwd=os.getcwd())
+    return completed_process.returncode == 0
+
+
+def setup_acts_for_cmd_or_die(cmd_str):
+    acts_framework_dir = os.path.abspath('acts_framework')
+    acts_setup_bin = os.path.join(acts_framework_dir, 'setup.py')
+    cmd = [sys.executable, acts_setup_bin, cmd_str]
+    subprocess.run(cmd, cwd=acts_framework_dir, check=True)
+
+
+def set_permissions_for_host_executables(outputs):
+    for file in outputs:
+        if os.path.basename(file) in host_executables:
+            current_mode = os.stat(file).st_mode
+            new_mode = current_mode | stat.S_IEXEC
+            os.chmod(file, new_mode)
+            log.log(log.INFO, "Changed file mode of %s from %s to %s" % (file, oct(current_mode), oct(new_mode)))
+
+
+class InstallLocalPackagesForInstallation(install):
+
+    user_options = install.user_options + [
+        ('reuse-acts', None, "Skip ACTS installation if already installed"),
+    ]
+    boolean_options = install.boolean_options + ['reuse-acts']
+
+    def initialize_options(self):
+        install.initialize_options(self)
+        self.reuse_acts = False
+
+    def run(self):
+        if self.reuse_acts and is_acts_importable():
+            self.announce('Reusing existing ACTS installation', log.WARN)
+        else:
+            self.announce('Installing ACTS for installation', log.WARN)
+            setup_acts_for_cmd_or_die("install")
+            self.announce('ACTS installed for installation.', log.WARN)
+        if not is_acts_importable():
+            raise DistutilsModuleError("Cannot import acts after installation")
+        install.run(self)
+        set_permissions_for_host_executables(self.get_outputs())
+
+
+def main():
+    # Relative path from calling directory to this file
+    our_dir = os.path.dirname(__file__)
+    # Must cd into this dir for package resolution to work
+    # This won't affect the calling shell
+    os.chdir(our_dir)
+    setup(
+        name='bluetooth_cert_tests',
+        version='1.0',
+        author='Android Open Source Project',
+        license='Apache2.0',
+        description="""Bluetooth Cert Tests Package""",
+        # Include root package so that bluetooth_packets_python3.so can be
+        # included as well
+        packages=[''] +
+        find_packages(exclude=['acts_framework', 'acts_framework.*', 'llvm_binutils', 'llvm_binutils.*']),
+        install_requires=install_requires,
+        package_data={
+            '': host_executables + ['*.so', 'lib64/*.so', 'target/*', 'llvm_binutils/bin/*', 'llvm_binutils/lib64/*'],
+            'cert': ['all_test_cases'],
+        },
+        cmdclass={
+            'install': InstallLocalPackagesForInstallation,
+        })
+
+
+if __name__ == '__main__':
+    main()
diff --git a/gd/shim/Android.bp b/gd/shim/Android.bp
index 33c9b65..25f8b9d 100644
--- a/gd/shim/Android.bp
+++ b/gd/shim/Android.bp
@@ -1,24 +1,22 @@
 filegroup {
     name: "BluetoothShimSources",
     srcs: [
-            "advertising.cc",
-            "controller.cc",
-            "connectability.cc",
-            "discoverability.cc",
-            "hci_layer.cc",
-            "inquiry.cc",
+            "dumpsys.cc",
             "l2cap.cc",
-            "name.cc",
-            "page.cc",
-            "scanning.cc",
-            "stack.cc",
-    ],
+    ]
 }
 
 filegroup {
     name: "BluetoothShimTestSources",
     srcs: [
+            "dumpsys_test.cc",
+            "l2cap_test.cc",
     ],
 }
 
-
+filegroup {
+    name: "BluetoothFacade_shim_layer",
+    srcs: [
+        "facade/facade.cc",
+    ],
+}
diff --git a/gd/shim/advertising.cc b/gd/shim/advertising.cc
deleted file mode 100644
index 0ac429a..0000000
--- a/gd/shim/advertising.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "bt_gd_shim"
-
-#include <functional>
-#include <memory>
-
-#include "hci/address.h"
-#include "hci/hci_packets.h"
-#include "hci/le_advertising_manager.h"
-#include "module.h"
-#include "os/handler.h"
-#include "os/log.h"
-#include "shim/advertising.h"
-
-namespace bluetooth {
-namespace shim {
-
-struct Advertising::impl {
-  impl(hci::LeAdvertisingManager* module, os::Handler* handler);
-  ~impl();
-
-  void StartAdvertising();
-  void StopAdvertising();
-
-  size_t GetNumberOfAdvertisingInstances() const;
-
- private:
-  void OnScan(hci::Address address, hci::AddressType address_type);
-  void OnTerminated(hci::ErrorCode code, uint8_t handle, uint8_t num_events);
-
-  hci::AdvertiserId advertiser_id_{hci::LeAdvertisingManager::kInvalidId};
-
-  hci::LeAdvertisingManager* advertising_manager_{nullptr};
-  os::Handler* handler_;
-};
-
-const ModuleFactory Advertising::Factory = ModuleFactory([]() { return new Advertising(); });
-
-Advertising::impl::impl(hci::LeAdvertisingManager* advertising_manager, os::Handler* handler)
-    : advertising_manager_(advertising_manager), handler_(handler) {}
-
-Advertising::impl::~impl() {}
-
-void Advertising::impl::StartAdvertising() {
-  if (advertiser_id_ == hci::LeAdvertisingManager::kInvalidId) {
-    LOG_WARN("%s Already advertising; please stop prior to starting again", __func__);
-    return;
-  }
-
-  hci::AdvertisingConfig config;
-  advertiser_id_ =
-      advertising_manager_->CreateAdvertiser(config, common::Bind(&impl::OnScan, common::Unretained(this)),
-                                             common::Bind(&impl::OnTerminated, common::Unretained(this)), handler_);
-  if (advertiser_id_ == hci::LeAdvertisingManager::kInvalidId) {
-    LOG_WARN("%s Unable to start advertising", __func__);
-    return;
-  }
-  LOG_DEBUG("%s Started advertising", __func__);
-}
-
-void Advertising::impl::StopAdvertising() {
-  if (advertiser_id_ == hci::LeAdvertisingManager::kInvalidId) {
-    LOG_WARN("%s No active advertising", __func__);
-    return;
-  }
-  advertising_manager_->RemoveAdvertiser(advertiser_id_);
-  advertiser_id_ = hci::LeAdvertisingManager::kInvalidId;
-  LOG_DEBUG("%s Stopped advertising", __func__);
-}
-
-void Advertising::impl::OnScan(hci::Address address, hci::AddressType address_type) {
-  LOG_INFO("%s UNIMPLEMENTED Received le advert from:%s", __func__, address.ToString().c_str());
-}
-
-void Advertising::impl::OnTerminated(hci::ErrorCode code, uint8_t handle, uint8_t num_events) {
-  LOG_INFO("%s UNIMPLEMENTED", __func__);
-}
-
-size_t Advertising::impl::GetNumberOfAdvertisingInstances() const {
-  return advertising_manager_->GetNumberOfAdvertisingInstances();
-}
-
-size_t Advertising::GetNumberOfAdvertisingInstances() const {
-  return pimpl_->GetNumberOfAdvertisingInstances();
-}
-
-void Advertising::StartAdvertising() {
-  pimpl_->StartAdvertising();
-}
-
-void Advertising::StopAdvertising() {
-  pimpl_->StopAdvertising();
-}
-
-/**
- * Module methods
- */
-void Advertising::ListDependencies(ModuleList* list) {
-  list->add<hci::LeAdvertisingManager>();
-}
-
-void Advertising::Start() {
-  pimpl_ = std::make_unique<impl>(GetDependency<hci::LeAdvertisingManager>(), GetHandler());
-}
-
-void Advertising::Stop() {
-  pimpl_.reset();
-}
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/cert/shim_test.py b/gd/shim/cert/shim_test.py
new file mode 100644
index 0000000..bc2fca6
--- /dev/null
+++ b/gd/shim/cert/shim_test.py
@@ -0,0 +1,37 @@
+#!/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.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
+
+
+class ShimTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(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())
diff --git a/gd/shim/cert/stack_test.py b/gd/shim/cert/stack_test.py
new file mode 100644
index 0000000..3daecc0
--- /dev/null
+++ b/gd/shim/cert/stack_test.py
@@ -0,0 +1,29 @@
+#!/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
+
+from cert.gd_base_test import GdBaseTestClass
+
+
+class StackTest(GdBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class(dut_module='SHIM', cert_module='SHIM')
+
+    def test_test(self):
+        return True
diff --git a/gd/shim/connectability.cc b/gd/shim/connectability.cc
deleted file mode 100644
index 2b779fd..0000000
--- a/gd/shim/connectability.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "bt_gd_shim"
-
-#include <memory>
-
-#include "common/bidi_queue.h"
-#include "hci/address.h"
-#include "hci/controller.h"
-#include "hci/hci_packets.h"
-#include "module.h"
-#include "neighbor/connectability.h"
-#include "os/handler.h"
-#include "os/log.h"
-#include "shim/connectability.h"
-
-namespace bluetooth {
-namespace shim {
-
-const ModuleFactory Connectability::Factory = ModuleFactory([]() { return new Connectability(); });
-
-struct Connectability::impl {
-  impl(neighbor::ConnectabilityModule* module) : module_(module) {}
-
-  neighbor::ConnectabilityModule* module_{nullptr};
-};
-
-void Connectability::StartConnectability() {
-  pimpl_->module_->StartConnectability();
-}
-
-void Connectability::StopConnectability() {
-  pimpl_->module_->StopConnectability();
-}
-
-bool Connectability::IsConnectable() const {
-  return pimpl_->module_->IsConnectable();
-}
-
-/**
- * Module methods
- */
-void Connectability::ListDependencies(ModuleList* list) {
-  list->add<neighbor::ConnectabilityModule>();
-}
-
-void Connectability::Start() {
-  pimpl_ = std::make_unique<impl>(GetDependency<neighbor::ConnectabilityModule>());
-}
-
-void Connectability::Stop() {
-  pimpl_.reset();
-}
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/connectability.h b/gd/shim/connectability.h
deleted file mode 100644
index 780b5be..0000000
--- a/gd/shim/connectability.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <memory>
-
-#include "module.h"
-#include "shim/iconnectability.h"
-
-namespace bluetooth {
-namespace shim {
-
-class Connectability : public bluetooth::Module, public bluetooth::shim::IConnectability {
- public:
-  void StartConnectability() override;
-  void StopConnectability() override;
-  bool IsConnectable() const override;
-
-  Connectability() = default;
-  ~Connectability() = default;
-
-  static const ModuleFactory Factory;
-
- protected:
-  void ListDependencies(ModuleList* list) override;  // Module
-  void Start() override;                             // Module
-  void Stop() override;                              // Module
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-  DISALLOW_COPY_AND_ASSIGN(Connectability);
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/controller.cc b/gd/shim/controller.cc
deleted file mode 100644
index a8eecc5..0000000
--- a/gd/shim/controller.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "bt_gd_shim"
-
-#include <memory>
-
-#include "common/bidi_queue.h"
-#include "hci/address.h"
-#include "hci/controller.h"
-#include "hci/hci_packets.h"
-#include "module.h"
-#include "os/handler.h"
-#include "os/log.h"
-#include "shim/controller.h"
-
-namespace bluetooth {
-namespace shim {
-
-const ModuleFactory Controller::Factory = ModuleFactory([]() { return new Controller(); });
-
-struct Controller::impl {
-  impl(hci::Controller* hci_controller) : hci_controller_(hci_controller) {}
-
-  hci::Controller* hci_controller_{nullptr};
-};
-
-bool Controller::IsCommandSupported(int op_code) const {
-  return pimpl_->hci_controller_->IsSupported((bluetooth::hci::OpCode)op_code);
-}
-
-uint16_t Controller::GetControllerAclPacketLength() const {
-  return pimpl_->hci_controller_->GetControllerAclPacketLength();
-}
-
-LeBufferSize Controller::GetControllerLeBufferSize() const {
-  LeBufferSize le_buffer_size;
-  hci::LeBufferSize hci_le_buffer_size = pimpl_->hci_controller_->GetControllerLeBufferSize();
-
-  le_buffer_size.le_data_packet_length = hci_le_buffer_size.le_data_packet_length_;
-  le_buffer_size.total_num_le_packets = hci_le_buffer_size.total_num_le_packets_;
-  return le_buffer_size;
-}
-
-LeMaximumDataLength Controller::GetControllerLeMaximumDataLength() const {
-  LeMaximumDataLength maximum_data_length;
-  hci::LeMaximumDataLength hci_maximum_data_length = pimpl_->hci_controller_->GetControllerLeMaximumDataLength();
-
-  maximum_data_length.supported_max_tx_octets = hci_maximum_data_length.supported_max_tx_octets_;
-  maximum_data_length.supported_max_tx_time = hci_maximum_data_length.supported_max_tx_time_;
-  maximum_data_length.supported_max_rx_octets = hci_maximum_data_length.supported_max_rx_octets_;
-  maximum_data_length.supported_max_rx_time = hci_maximum_data_length.supported_max_rx_time_;
-  return maximum_data_length;
-}
-
-uint16_t Controller::GetControllerNumAclPacketBuffers() const {
-  return pimpl_->hci_controller_->GetControllerNumAclPacketBuffers();
-}
-
-uint64_t Controller::GetControllerLeLocalSupportedFeatures() const {
-  return pimpl_->hci_controller_->GetControllerLeLocalSupportedFeatures();
-}
-
-uint64_t Controller::GetControllerLocalExtendedFeatures(uint8_t page_number) const {
-  return pimpl_->hci_controller_->GetControllerLocalExtendedFeatures(page_number);
-}
-
-std::string Controller::GetControllerMacAddress() const {
-  return pimpl_->hci_controller_->GetControllerMacAddress().ToString();
-}
-
-uint64_t Controller::GetControllerLeSupportedStates() const {
-  return pimpl_->hci_controller_->GetControllerLeSupportedStates();
-}
-
-uint8_t Controller::GetControllerLeNumberOfSupportedAdverisingSets() const {
-  return pimpl_->hci_controller_->GetControllerLeNumberOfSupportedAdverisingSets();
-}
-
-uint8_t Controller::GetControllerLocalExtendedFeaturesMaxPageNumber() const {
-  return pimpl_->hci_controller_->GetControllerLocalExtendedFeaturesMaxPageNumber();
-}
-
-/**
- * Module methods
- */
-void Controller::ListDependencies(ModuleList* list) {
-  list->add<hci::Controller>();
-}
-
-void Controller::Start() {
-  LOG_INFO("%s Starting controller shim layer", __func__);
-  pimpl_ = std::make_unique<impl>(GetDependency<hci::Controller>());
-}
-
-void Controller::Stop() {
-  pimpl_.reset();
-}
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/controller.h b/gd/shim/controller.h
deleted file mode 100644
index a31a629..0000000
--- a/gd/shim/controller.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <memory>
-#include <string>
-
-#include "module.h"
-#include "shim/icontroller.h"
-
-/**
- * Gd shim controller module that depends upon the Gd controller module.
- *
- * Wraps the Gd controller module to expose a sufficient API to allow
- * proper operation of the legacy shim controller interface.
- *
- */
-namespace bluetooth {
-namespace shim {
-
-class Controller : public bluetooth::Module, public bluetooth::shim::IController {
- public:
-  Controller() = default;
-  ~Controller() = default;
-
-  static const ModuleFactory Factory;
-
-  // Exported controller methods from IController for shim layer
-  bool IsCommandSupported(int op_code) const override;
-  LeBufferSize GetControllerLeBufferSize() const override;
-  LeMaximumDataLength GetControllerLeMaximumDataLength() const override;
-  std::string GetControllerMacAddress() const override;
-  uint16_t GetControllerAclPacketLength() const override;
-  uint16_t GetControllerNumAclPacketBuffers() const override;
-  uint64_t GetControllerLeLocalSupportedFeatures() const override;
-  uint64_t GetControllerLeSupportedStates() const override;
-  uint64_t GetControllerLocalExtendedFeatures(uint8_t page_number) const override;
-  uint8_t GetControllerLeNumberOfSupportedAdverisingSets() const override;
-  uint8_t GetControllerLocalExtendedFeaturesMaxPageNumber() const override;
-
- protected:
-  void ListDependencies(ModuleList* list) override;  // Module
-  void Start() override;                             // Module
-  void Stop() override;                              // Module
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-  DISALLOW_COPY_AND_ASSIGN(Controller);
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/discoverability.cc b/gd/shim/discoverability.cc
deleted file mode 100644
index f793404..0000000
--- a/gd/shim/discoverability.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "bt_gd_shim"
-
-#include <memory>
-
-#include "common/bidi_queue.h"
-#include "hci/address.h"
-#include "hci/controller.h"
-#include "hci/hci_packets.h"
-#include "module.h"
-#include "neighbor/discoverability.h"
-#include "os/handler.h"
-#include "os/log.h"
-#include "shim/discoverability.h"
-
-namespace bluetooth {
-namespace shim {
-
-const ModuleFactory Discoverability::Factory = ModuleFactory([]() { return new Discoverability(); });
-
-struct Discoverability::impl {
-  impl(neighbor::DiscoverabilityModule* module) : module_(module) {}
-
-  neighbor::DiscoverabilityModule* module_{nullptr};
-
-  bool general_discoverability_enabled_{false};
-  bool limited_discoverability_enabled_{false};
-};
-
-void Discoverability::StopDiscoverability() {
-  if (pimpl_->general_discoverability_enabled_ || pimpl_->limited_discoverability_enabled_) {
-    pimpl_->module_->StopDiscoverability();
-    LOG_DEBUG("%s Stopped discoverability", __func__);
-  } else {
-    LOG_WARN("%s Discoverability not enabled", __func__);
-  }
-}
-
-void Discoverability::StartLimitedDiscoverability() {
-  if (pimpl_->general_discoverability_enabled_ || pimpl_->limited_discoverability_enabled_) {
-    LOG_WARN("%s Please stop discoverability before re-enabling", __func__);
-    return;
-  }
-  pimpl_->module_->StartLimitedDiscoverability();
-  LOG_DEBUG("%s Started limited discoverability", __func__);
-}
-
-void Discoverability::StartGeneralDiscoverability() {
-  if (pimpl_->general_discoverability_enabled_ || pimpl_->limited_discoverability_enabled_) {
-    LOG_WARN("%s Please stop discoverability before re-enabling", __func__);
-    return;
-  }
-  pimpl_->module_->StartGeneralDiscoverability();
-  LOG_DEBUG("%s Started general discoverability", __func__);
-}
-
-bool Discoverability::IsGeneralDiscoverabilityEnabled() const {
-  return pimpl_->general_discoverability_enabled_;
-}
-
-bool Discoverability::IsLimitedDiscoverabilityEnabled() const {
-  return pimpl_->limited_discoverability_enabled_;
-}
-
-/**
- * Module methods
- */
-void Discoverability::ListDependencies(ModuleList* list) {
-  list->add<neighbor::DiscoverabilityModule>();
-}
-
-void Discoverability::Start() {
-  pimpl_ = std::make_unique<impl>(GetDependency<neighbor::DiscoverabilityModule>());
-}
-
-void Discoverability::Stop() {
-  pimpl_.reset();
-}
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/discoverability.h b/gd/shim/discoverability.h
deleted file mode 100644
index 34ad49a..0000000
--- a/gd/shim/discoverability.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <memory>
-
-#include "module.h"
-#include "shim/idiscoverability.h"
-
-namespace bluetooth {
-namespace shim {
-
-class Discoverability : public bluetooth::Module, public bluetooth::shim::IDiscoverability {
- public:
-  void StartGeneralDiscoverability() override;
-  void StartLimitedDiscoverability() override;
-  void StopDiscoverability() override;
-
-  bool IsGeneralDiscoverabilityEnabled() const override;
-  bool IsLimitedDiscoverabilityEnabled() const override;
-
-  Discoverability() = default;
-  ~Discoverability() = default;
-
-  static const ModuleFactory Factory;
-
- protected:
-  void ListDependencies(ModuleList* list) override;  // Module
-  void Start() override;                             // Module
-  void Stop() override;                              // Module
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-  DISALLOW_COPY_AND_ASSIGN(Discoverability);
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/dumpsys.cc b/gd/shim/dumpsys.cc
new file mode 100644
index 0000000..af53c0d
--- /dev/null
+++ b/gd/shim/dumpsys.cc
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "bt_gd_shim"
+
+#include "shim/dumpsys.h"
+
+#include <algorithm>
+#include <functional>
+#include <future>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include "bundler_generated.h"
+#include "dumpsys_generated.h"
+#include "dumpsys_module_schema_data.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/reflection_generated.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace shim {
+
+namespace {
+constexpr char kModuleName[] = "shim::Dumpsys";
+constexpr char kDumpsysTitle[] = "----- Gd Dumpsys ------";
+}  // namespace
+
+constexpr char kArgumentDeveloper[] = "--dev";
+
+class ParsedDumpsysArgs {
+ public:
+  ParsedDumpsysArgs(const char** args) {
+    if (args == nullptr) return;
+    const char* p = *args;
+    while (p != nullptr) {
+      num_args_++;
+      if (!strcmp(p, kArgumentDeveloper)) {
+        dev_arg_ = true;
+      } else {
+        // silently ignore unexpected option
+      }
+      if (++args == nullptr) break;
+      p = *args;
+    }
+  }
+  bool IsDeveloper() const {
+    return dev_arg_;
+  }
+
+ private:
+  unsigned num_args_{0};
+  bool dev_arg_{false};
+};
+
+struct Dumpsys::impl {
+ public:
+  void DumpWithArgs(int fd, const char** args, std::promise<void> promise);
+
+  impl(const Dumpsys& dumpsys_module, const std::string& bundled_schema_data);
+  ~impl() = default;
+
+  int GetNumberOfBundledSchemas() const;
+
+ protected:
+  void FilterAsUser(std::string* dumpsys_data);
+  void FilterAsDeveloper(std::string* dumpsys_data);
+  std::string PrintAsJson(std::string* dumpsys_data) const;
+
+ private:
+  const reflection::Schema* FindInBundledSchema(const std::string& name) const;
+  const dumpsys::BundledSchema* GetBundledSchema() const;
+  const Dumpsys& dumpsys_module_;
+  const std::string pre_bundled_schema_;
+};
+
+const ModuleFactory Dumpsys::Factory =
+    ModuleFactory([]() { return new Dumpsys(bluetooth::dumpsys::GetBundledSchemaData()); });
+
+Dumpsys::impl::impl(const Dumpsys& dumpsys_module, const std::string& pre_bundled_schema)
+    : dumpsys_module_(dumpsys_module), pre_bundled_schema_(pre_bundled_schema) {}
+
+int Dumpsys::impl::GetNumberOfBundledSchemas() const {
+  return GetBundledSchema()->map()->size();
+}
+
+const dumpsys::BundledSchema* Dumpsys::impl::GetBundledSchema() const {
+  const dumpsys::BundledSchema* bundled_schema =
+      flatbuffers::GetRoot<dumpsys::BundledSchema>(pre_bundled_schema_.data());
+  ASSERT(bundled_schema != nullptr);
+  return bundled_schema;
+}
+
+const reflection::Schema* Dumpsys::impl::FindInBundledSchema(const std::string& name) const {
+  const flatbuffers::Vector<flatbuffers::Offset<dumpsys::BundledSchemaMap>>* map = GetBundledSchema()->map();
+
+  for (auto it = map->cbegin(); it != map->cend(); ++it) {
+    if (it->name()->str() == name) {
+      flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(it->data()->Data()), it->data()->size());
+      if (!reflection::VerifySchemaBuffer(verifier)) {
+        LOG_WARN("Unable to verify schema buffer name:%s", name.c_str());
+        return nullptr;
+      }
+      return reflection::GetSchema(it->data()->Data());
+    }
+  }
+
+  LOG_WARN("Unable to find bundled schema name:%s", name.c_str());
+  LOG_WARN("  title:%s root_name:%s", GetBundledSchema()->title()->c_str(), GetBundledSchema()->root_name()->c_str());
+  for (auto it = map->cbegin(); it != map->cend(); ++it) {
+    LOG_WARN("    schema:%s", it->name()->c_str());
+  }
+  return nullptr;
+}
+
+void Dumpsys::impl::FilterAsDeveloper(std::string* dumpsys_data) {
+  ASSERT(dumpsys_data != nullptr);
+  LOG_INFO("%s UNIMPLEMENTED", __func__);
+}
+
+void Dumpsys::impl::FilterAsUser(std::string* dumpsys_data) {
+  ASSERT(dumpsys_data != nullptr);
+  LOG_INFO("%s UNIMPLEMENTED", __func__);
+}
+
+std::string Dumpsys::impl::PrintAsJson(std::string* dumpsys_data) const {
+  ASSERT(dumpsys_data != nullptr);
+
+  const flatbuffers::String* root_name = GetBundledSchema()->root_name();
+  if (root_name == nullptr) {
+    char buf[255];
+    snprintf(buf, sizeof(buf), "ERROR: Unable to find root name in prebundled schema\n");
+    return std::string(buf);
+  }
+
+  const reflection::Schema* schema = FindInBundledSchema(root_name->str());
+  if (schema == nullptr) {
+    char buf[255];
+    snprintf(buf, sizeof(buf), "ERROR: Unable to find schema root name:%s\n", root_name->c_str());
+    return std::string(buf);
+  }
+
+  flatbuffers::Parser parser;
+  if (!parser.Deserialize(schema)) {
+    char buf[255];
+    snprintf(buf, sizeof(buf), "ERROR: Unable to deserialize bundle root name:%s\n", root_name->c_str());
+    return std::string(buf);
+  }
+
+  std::string jsongen;
+  flatbuffers::GenerateText(parser, dumpsys_data->data(), &jsongen);
+  return jsongen;
+}
+
+void Dumpsys::impl::DumpWithArgs(int fd, const char** args, std::promise<void> promise) {
+  ParsedDumpsysArgs parsed_dumpsys_args(args);
+  const auto registry = dumpsys_module_.GetModuleRegistry();
+
+  ModuleDumper dumper(*registry, kDumpsysTitle);
+  std::string dumpsys_data;
+  dumper.DumpState(&dumpsys_data);
+
+  if (parsed_dumpsys_args.IsDeveloper()) {
+    dprintf(fd, " ----- Filtering as Developer -----\n");
+    FilterAsDeveloper(&dumpsys_data);
+  } else {
+    dprintf(fd, " ----- Filtering as User -----\n");
+    FilterAsUser(&dumpsys_data);
+  }
+
+  dprintf(fd, "%s", PrintAsJson(&dumpsys_data).c_str());
+  promise.set_value();
+}
+
+Dumpsys::Dumpsys(const std::string& pre_bundled_schema) : pre_bundled_schema_(pre_bundled_schema) {}
+
+void Dumpsys::Dump(int fd, const char** args) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  CallOn(pimpl_.get(), &Dumpsys::impl::DumpWithArgs, fd, args, std::move(promise));
+  future.get();
+}
+
+os::Handler* Dumpsys::GetGdShimHandler() {
+  return GetHandler();
+}
+
+/**
+ * Module methods
+ */
+void Dumpsys::ListDependencies(ModuleList* list) {}
+
+void Dumpsys::Start() {
+  pimpl_ = std::make_unique<impl>(*this, pre_bundled_schema_);
+}
+
+void Dumpsys::Stop() {
+  pimpl_.reset();
+}
+
+DumpsysDataFinisher Dumpsys::GetDumpsysData(flatbuffers::FlatBufferBuilder* fb_builder) const {
+  auto name = fb_builder->CreateString("----- Shim Dumpsys -----");
+  auto example_piecemeal_string = fb_builder->CreateString("Example Piecemeal String");
+  auto example_instant_string = fb_builder->CreateString("Example Instant String");
+
+  ExamplePiecemealTableBuilder example_piecemeal_table_builder(*fb_builder);
+  example_piecemeal_table_builder.add_example_string(example_piecemeal_string);
+  example_piecemeal_table_builder.add_example_int(123);
+  example_piecemeal_table_builder.add_example_float(1.23);
+  auto example_piecemeal_table = example_piecemeal_table_builder.Finish();
+
+  auto example_instant_table = CreateExampleInstantTable(*fb_builder, example_instant_string, 246, 2.46);
+
+  DumpsysModuleDataBuilder builder(*fb_builder);
+  builder.add_title(name);
+  builder.add_number_of_bundled_schemas(pimpl_->GetNumberOfBundledSchemas());
+  builder.add_example_piecemeal_table(example_piecemeal_table);
+  builder.add_example_instant_table(example_instant_table);
+  auto dumpsys_data = builder.Finish();
+
+  return [dumpsys_data](DumpsysDataBuilder* builder) { builder->add_shim_dumpsys_data(dumpsys_data); };
+}
+
+std::string Dumpsys::ToString() const {
+  return kModuleName;
+}
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/shim/dumpsys.fbs b/gd/shim/dumpsys.fbs
new file mode 100644
index 0000000..40ca1f5
--- /dev/null
+++ b/gd/shim/dumpsys.fbs
@@ -0,0 +1,25 @@
+// shim::dumpsys data
+namespace bluetooth.shim;
+
+attribute "privacy";
+
+table ExamplePiecemealTable {
+    example_string:string (privacy:"Any");
+    example_int:int (privacy:"Any");
+    example_float:float (privacy:"Any");
+}
+
+table ExampleInstantTable {
+    example_string:string (privacy:"Any");
+    example_int:int (privacy:"Any");
+    example_float:float (privacy:"Any");
+}
+
+table DumpsysModuleData {
+    title:string (privacy:"Any");
+    number_of_bundled_schemas:int (privacy:"Any");
+    example_piecemeal_table:ExamplePiecemealTable (privacy:"Any");
+    example_instant_table:ExampleInstantTable (privacy:"Any");
+}
+
+root_type DumpsysModuleData;
diff --git a/gd/shim/advertising.h b/gd/shim/dumpsys.h
similarity index 68%
rename from gd/shim/advertising.h
rename to gd/shim/dumpsys.h
index 27f8e07..83cd5e1 100644
--- a/gd/shim/advertising.h
+++ b/gd/shim/dumpsys.h
@@ -19,20 +19,19 @@
 #include <string>
 
 #include "module.h"
-#include "shim/iadvertising.h"
 
 namespace bluetooth {
 namespace shim {
 
-class Advertising : public bluetooth::Module, public bluetooth::shim::IAdvertising {
+class Dumpsys : public bluetooth::Module {
  public:
-  Advertising() = default;
-  ~Advertising() = default;
+  void Dump(int fd, const char** args);
 
-  void StartAdvertising() override;
-  void StopAdvertising() override;
+  // Convenience thread used by shim layer for task execution
+  os::Handler* GetGdShimHandler();
 
-  size_t GetNumberOfAdvertisingInstances() const override;
+  Dumpsys(const std::string& pre_bundled_schema);
+  ~Dumpsys() = default;
 
   static const ModuleFactory Factory;
 
@@ -40,11 +39,14 @@
   void ListDependencies(ModuleList* list) override;  // Module
   void Start() override;                             // Module
   void Stop() override;                              // Module
+  std::string ToString() const override;             // Module
+  DumpsysDataFinisher GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const override;  // Module
 
  private:
   struct impl;
   std::unique_ptr<impl> pimpl_;
-  DISALLOW_COPY_AND_ASSIGN(Advertising);
+  const std::string& pre_bundled_schema_;
+  DISALLOW_COPY_AND_ASSIGN(Dumpsys);
 };
 
 }  // namespace shim
diff --git a/gd/shim/dumpsys_test.cc b/gd/shim/dumpsys_test.cc
new file mode 100644
index 0000000..8f70288
--- /dev/null
+++ b/gd/shim/dumpsys_test.cc
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#include "shim/dumpsys.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "module.h"
+#include "os/thread.h"
+#include "test_gen/dumpsys_test_data_bin.h"
+
+namespace testing {
+
+using bluetooth::TestModuleRegistry;
+using namespace bluetooth;
+
+namespace {
+
+bool SimpleJsonValidator(int fd) {
+  char buf{0};
+  bool within_double_quotes{false};
+  int left_bracket{0}, right_bracket{0};
+  while (read(fd, &buf, 1) != -1) {
+    switch (buf) {
+      case '"':
+        within_double_quotes = !within_double_quotes;
+        break;
+      case '{':
+        if (!within_double_quotes) {
+          left_bracket++;
+        }
+        break;
+      case '}':
+        if (!within_double_quotes) {
+          right_bracket++;
+        }
+        break;
+      default:
+        break;
+    }
+  }
+  return left_bracket == right_bracket;
+}
+
+}  // namespace
+
+// To create dumpsys_test_header_bin.h:
+// make bluetooth_flatbuffer_bundler
+// ${ANDROID_BUILD_TOP}/out/host/linux-x86/bin/bluetooth_flatbuffer_bundler -w -m bluetooth.DumpsysData -f
+// test_gen/dumpsys_test_data_bin -n bluetooth::test test_gen/*
+
+class DumpsysTest : public Test {
+ protected:
+  void SetUp() override {
+    dumpsys_module_ = new bluetooth::shim::Dumpsys(bluetooth::test::GetBundledSchemaData());
+    fake_registry_.InjectTestModule(&shim::Dumpsys::Factory, dumpsys_module_);
+  }
+
+  void TearDown() override {
+    fake_registry_.StopAll();
+  }
+
+  TestModuleRegistry fake_registry_;
+  os::Thread& thread_ = fake_registry_.GetTestThread();
+  bluetooth::shim::Dumpsys* dumpsys_module_ = nullptr;
+  os::Handler* client_handler_ = nullptr;
+};
+
+TEST_F(DumpsysTest, dump) {
+  int sv[2];
+  int rc = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0, sv);
+  ASSERT(rc == 0);
+  dumpsys_module_->Dump(sv[0], nullptr);
+  ASSERT(SimpleJsonValidator(sv[1]));
+}
+
+}  // namespace testing
diff --git a/gd/shim/facade/facade.cc b/gd/shim/facade/facade.cc
new file mode 100644
index 0000000..a07329a
--- /dev/null
+++ b/gd/shim/facade/facade.cc
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#include "shim/facade/facade.h"
+
+#include <memory>
+
+#include "common/bind.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/reflection_generated.h"
+#include "grpc/grpc_event_queue.h"
+#include "os/log.h"
+#include "shim/dumpsys.h"
+#include "shim/facade/facade.grpc.pb.h"
+#include "shim/facade/facade.pb.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+namespace bluetooth {
+namespace shim {
+namespace facade {
+
+class ShimFacadeService : public ShimFacade::Service {
+ public:
+  ShimFacadeService(shim::Dumpsys* dumpsys_layer, ::bluetooth::os::Handler* facade_handler)
+      : dumpsys_layer_(dumpsys_layer), facade_handler_(facade_handler) {}
+
+  virtual ~ShimFacadeService() {}
+
+  ::grpc::Status Dump(
+      ::grpc::ServerContext* context,
+      const ::google::protobuf::Empty* request,
+      ::grpc::ServerWriter<DumpsysMsg>* writer) override {
+    dumpsys_layer_->Dump(0, nullptr);
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  shim::Dumpsys* dumpsys_layer_{nullptr};
+  [[maybe_unused]] ::bluetooth::os::Handler* facade_handler_{nullptr};
+};
+
+void ShimFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<Dumpsys>();
+}
+
+void ShimFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new ShimFacadeService(GetDependency<Dumpsys>(), GetHandler());
+}
+
+void ShimFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* ShimFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory ShimFacadeModule::Factory = ::bluetooth::ModuleFactory([]() { return new ShimFacadeModule(); });
+
+}  // namespace facade
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/hci/cert/cert.h b/gd/shim/facade/facade.h
similarity index 75%
copy from gd/hci/cert/cert.h
copy to gd/shim/facade/facade.h
index e3ecf46..2534532 100644
--- a/gd/hci/cert/cert.h
+++ b/gd/shim/facade/facade.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -19,15 +19,15 @@
 #include <grpc++/grpc++.h>
 
 #include "grpc/grpc_module.h"
-#include "hci/acl_manager.h"
+#include "shim/dumpsys.h"
 
 namespace bluetooth {
-namespace hci {
-namespace cert {
+namespace shim {
+namespace facade {
 
-class AclManagerCertService;
+class ShimFacadeService;
 
-class AclManagerCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class ShimFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
@@ -37,9 +37,9 @@
   ::grpc::Service* GetService() const override;
 
  private:
-  AclManagerCertService* service_;
+  ShimFacadeService* service_;
 };
 
-}  // namespace cert
-}  // namespace hci
+}  // namespace facade
+}  // namespace shim
 }  // namespace bluetooth
diff --git a/gd/shim/facade/facade.proto b/gd/shim/facade/facade.proto
new file mode 100644
index 0000000..18c0340
--- /dev/null
+++ b/gd/shim/facade/facade.proto
@@ -0,0 +1,13 @@
+syntax = "proto3";
+
+package bluetooth.shim.facade;
+
+import "google/protobuf/empty.proto";
+
+service ShimFacade {
+  rpc Dump(google.protobuf.Empty) returns (stream DumpsysMsg) {}
+}
+
+message DumpsysMsg {
+  bytes data = 1;
+}
diff --git a/gd/shim/hci_layer.cc b/gd/shim/hci_layer.cc
deleted file mode 100644
index e11454b..0000000
--- a/gd/shim/hci_layer.cc
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "bt_gd_shim"
-
-#include <cstdint>
-#include <memory>
-#include <queue>
-#include <unordered_map>
-#include <vector>
-
-#include "hci/hci_layer.h"
-#include "hci/hci_packets.h"
-#include "module.h"
-#include "os/handler.h"
-#include "os/log.h"
-#include "packet/raw_builder.h"
-#include "shim/hci_layer.h"
-
-namespace bluetooth {
-namespace shim {
-
-using TokenQueue = std::queue<const void*>;
-using OpCodeTokenQueueMap = std::unordered_map<hci::OpCode, TokenQueue>;
-
-const ModuleFactory HciLayer::Factory = ModuleFactory([]() { return new HciLayer(); });
-
-struct HciLayer::impl {
-  impl(os::Handler* handler, hci::HciLayer* hci_layer) : handler_(handler), hci_layer_(hci_layer) {}
-
-  void OnTransmitPacketCommandComplete(hci::CommandCompleteView view) {
-    if (command_complete_callback_ == nullptr) {
-      LOG_WARN("%s Received packet complete with no complete callback registered", __func__);
-      return;
-    }
-
-    uint16_t command_op_code = static_cast<uint16_t>(view.GetCommandOpCode());
-    std::vector<const uint8_t> data(view.begin(), view.end());
-
-    if (op_code_token_queue_map_.count(view.GetCommandOpCode()) == 0) {
-      LOG_WARN("%s Received unexpected command complete for opcode:0x%04x", __func__, command_op_code);
-      return;
-    }
-    const void* token = op_code_token_queue_map_[view.GetCommandOpCode()].front();
-    if (token == nullptr) {
-      LOG_WARN("%s Received expected command status but no token for opcode:0x%04x", __func__, command_op_code);
-      return;
-    }
-
-    op_code_token_queue_map_[view.GetCommandOpCode()].pop();
-    command_complete_callback_(command_op_code, data, token);
-  }
-
-  void OnTransmitPacketStatus(hci::CommandStatusView view) {
-    if (command_status_callback_ == nullptr) {
-      LOG_WARN("%s Received packet complete with no status callback registered", __func__);
-      return;
-    }
-
-    uint16_t command_op_code = static_cast<uint16_t>(view.GetCommandOpCode());
-    std::vector<const uint8_t> data(view.begin(), view.end());
-
-    if (op_code_token_queue_map_.count(view.GetCommandOpCode()) == 0) {
-      LOG_WARN("%s Received unexpected command status for opcode:0x%04x", __func__, command_op_code);
-      return;
-    }
-    const void* token = op_code_token_queue_map_[view.GetCommandOpCode()].front();
-    if (token == nullptr) {
-      LOG_WARN("%s Received expected command status but no token for opcode:0x%04x", __func__, command_op_code);
-      return;
-    }
-
-    op_code_token_queue_map_[view.GetCommandOpCode()].pop();
-    uint8_t status = static_cast<uint8_t>(view.GetStatus());
-    command_status_callback_(command_op_code, data, token, status);
-  }
-
-  void TransmitCommand(uint16_t command, const uint8_t* data, size_t len, const void* token) {
-    ASSERT(data != nullptr);
-    ASSERT(token != nullptr);
-
-    const hci::OpCode op_code = static_cast<const hci::OpCode>(command);
-
-    auto payload = MakeUniquePacket(data, len);
-    auto packet = hci::CommandPacketBuilder::Create(op_code, std::move(payload));
-
-    op_code_token_queue_map_[op_code].push(token);
-    if (IsCommandStatusOpcode(op_code)) {
-      hci_layer_->EnqueueCommand(std::move(packet),
-                                 common::BindOnce(&impl::OnTransmitPacketStatus, common::Unretained(this)), handler_);
-    } else {
-      hci_layer_->EnqueueCommand(std::move(packet),
-                                 common::BindOnce(&impl::OnTransmitPacketCommandComplete, common::Unretained(this)),
-                                 handler_);
-    }
-  }
-
-  void RegisterCommandComplete(CommandCompleteCallback callback) {
-    ASSERT(command_complete_callback_ == nullptr);
-    command_complete_callback_ = callback;
-  }
-
-  void UnregisterCommandComplete() {
-    ASSERT(command_complete_callback_ != nullptr);
-    command_complete_callback_ = nullptr;
-  }
-
-  void RegisterCommandStatus(CommandStatusCallback callback) {
-    ASSERT(command_status_callback_ == nullptr);
-    command_status_callback_ = callback;
-  }
-
-  void UnregisterCommandStatus() {
-    ASSERT(command_status_callback_ != nullptr);
-    command_status_callback_ = nullptr;
-  }
-
- private:
-  os::Handler* handler_{nullptr};
-  hci::HciLayer* hci_layer_{nullptr};
-
-  CommandCompleteCallback command_complete_callback_;
-  CommandStatusCallback command_status_callback_;
-
-  OpCodeTokenQueueMap op_code_token_queue_map_;
-
-  /**
-   * Returns true if expecting command complete, false otherwise
-   */
-  bool IsCommandStatusOpcode(hci::OpCode op_code) {
-    switch (op_code) {
-      case hci::OpCode::INQUIRY:
-      case hci::OpCode::CREATE_CONNECTION:
-      case hci::OpCode::DISCONNECT:
-      case hci::OpCode::ACCEPT_CONNECTION_REQUEST:
-      case hci::OpCode::REJECT_CONNECTION_REQUEST:
-      case hci::OpCode::CHANGE_CONNECTION_PACKET_TYPE:
-      case hci::OpCode::AUTHENTICATION_REQUESTED:
-      case hci::OpCode::SET_CONNECTION_ENCRYPTION:
-      case hci::OpCode::CHANGE_CONNECTION_LINK_KEY:
-      case hci::OpCode::MASTER_LINK_KEY:
-      case hci::OpCode::REMOTE_NAME_REQUEST:
-      case hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES:
-      case hci::OpCode::READ_REMOTE_EXTENDED_FEATURES:
-      case hci::OpCode::READ_REMOTE_VERSION_INFORMATION:
-      case hci::OpCode::READ_CLOCK_OFFSET:
-      case hci::OpCode::SETUP_SYNCHRONOUS_CONNECTION:
-      case hci::OpCode::ACCEPT_SYNCHRONOUS_CONNECTION:
-      case hci::OpCode::REJECT_SYNCHRONOUS_CONNECTION:
-      case hci::OpCode::ENHANCED_SETUP_SYNCHRONOUS_CONNECTION:
-      case hci::OpCode::ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION:
-      case hci::OpCode::HOLD_MODE:
-      case hci::OpCode::SNIFF_MODE:
-      case hci::OpCode::EXIT_SNIFF_MODE:
-      case hci::OpCode::QOS_SETUP:
-      case hci::OpCode::SWITCH_ROLE:
-      case hci::OpCode::FLOW_SPECIFICATION:
-      case hci::OpCode::REFRESH_ENCRYPTION_KEY:
-      case hci::OpCode::LE_CREATE_CONNECTION:
-      case hci::OpCode::LE_CONNECTION_UPDATE:
-      case hci::OpCode::LE_READ_REMOTE_FEATURES:
-      case hci::OpCode::LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND:
-      case hci::OpCode::LE_GENERATE_DHKEY_COMMAND:
-      case hci::OpCode::LE_SET_PHY:
-      case hci::OpCode::LE_EXTENDED_CREATE_CONNECTION:
-      case hci::OpCode::LE_PERIODIC_ADVERTISING_CREATE_SYNC:
-        return true;
-      default:
-        return false;
-    }
-  }
-
-  std::unique_ptr<packet::RawBuilder> MakeUniquePacket(const uint8_t* data, size_t len) {
-    packet::RawBuilder builder;
-    std::vector<uint8_t> bytes(data, data + len);
-
-    auto payload = std::make_unique<packet::RawBuilder>();
-    payload->AddOctets(bytes);
-
-    return payload;
-  }
-};
-
-void HciLayer::TransmitCommand(uint16_t op_code, const uint8_t* data, size_t len, const void* token) {
-  pimpl_->TransmitCommand(op_code, data, len, std::move(token));
-}
-
-void HciLayer::RegisterCommandComplete(CommandCompleteCallback callback) {
-  pimpl_->RegisterCommandComplete(callback);
-}
-
-void HciLayer::UnregisterCommandComplete() {
-  pimpl_->UnregisterCommandComplete();
-}
-
-void HciLayer::RegisterCommandStatus(CommandStatusCallback callback) {
-  pimpl_->RegisterCommandStatus(callback);
-}
-
-void HciLayer::UnregisterCommandStatus() {
-  pimpl_->UnregisterCommandStatus();
-}
-
-/**
- * Module methods
- */
-void HciLayer::ListDependencies(ModuleList* list) {
-  list->add<hci::HciLayer>();
-}
-
-void HciLayer::Start() {
-  LOG_INFO("%s Starting controller shim layer", __func__);
-  pimpl_ = std::make_unique<impl>(GetHandler(), GetDependency<hci::HciLayer>());
-}
-
-void HciLayer::Stop() {
-  pimpl_.reset();
-}
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/hci_layer.h b/gd/shim/hci_layer.h
deleted file mode 100644
index bcd7fed..0000000
--- a/gd/shim/hci_layer.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-#include <string>
-
-#include "module.h"
-#include "shim/ihci_layer.h"
-
-/**
- * The hci layer shim module that depends on the Gd hci layer module.
- */
-namespace bluetooth {
-namespace shim {
-
-class HciLayer : public ::bluetooth::Module, public ::bluetooth::shim::IHciLayer {
- public:
-  HciLayer() = default;
-  ~HciLayer() = default;
-
-  void TransmitCommand(uint16_t op_code, const uint8_t* data, size_t len,
-                       const void* token);  // IHciLayer
-
-  void RegisterCommandComplete(CommandCompleteCallback callback);  // IHciLayer
-  void UnregisterCommandComplete();                                // IHciLayer
-
-  void RegisterCommandStatus(CommandStatusCallback callback);  // IHciLayer
-  void UnregisterCommandStatus();                              // IHciLayer
-
-  static const ModuleFactory Factory;
-
- protected:
-  void ListDependencies(ModuleList* list) override;  // Module
-  void Start() override;                             // Module
-  void Stop() override;                              // Module
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-  DISALLOW_COPY_AND_ASSIGN(HciLayer);
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/iadvertising.h b/gd/shim/iadvertising.h
deleted file mode 100644
index 3b90b37..0000000
--- a/gd/shim/iadvertising.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <cstddef>
-
-/**
- * The gd API exported to the legacy api
- */
-namespace bluetooth {
-namespace shim {
-
-struct IAdvertising {
-  virtual void StartAdvertising() = 0;
-  virtual void StopAdvertising() = 0;
-
-  virtual size_t GetNumberOfAdvertisingInstances() const = 0;
-
-  virtual ~IAdvertising() {}
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/icontroller.h b/gd/shim/icontroller.h
deleted file mode 100644
index 988bb75..0000000
--- a/gd/shim/icontroller.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <cstdint>
-#include <string>
-
-/**
- * The shim controller module that depends on the Gd controller module
- */
-namespace bluetooth {
-namespace shim {
-
-typedef struct {
-  uint16_t le_data_packet_length;
-  uint8_t total_num_le_packets;
-} LeBufferSize;
-
-typedef struct {
-  uint16_t supported_max_tx_octets;
-  uint16_t supported_max_tx_time;
-  uint16_t supported_max_rx_octets;
-  uint16_t supported_max_rx_time;
-} LeMaximumDataLength;
-
-struct IController {
-  virtual bool IsCommandSupported(int op_code) const = 0;
-  virtual LeBufferSize GetControllerLeBufferSize() const = 0;
-  virtual LeMaximumDataLength GetControllerLeMaximumDataLength() const = 0;
-  virtual std::string GetControllerMacAddress() const = 0;
-  virtual uint16_t GetControllerAclPacketLength() const = 0;
-  virtual uint16_t GetControllerNumAclPacketBuffers() const = 0;
-  virtual uint64_t GetControllerLeLocalSupportedFeatures() const = 0;
-  virtual uint64_t GetControllerLeSupportedStates() const = 0;
-  virtual uint64_t GetControllerLocalExtendedFeatures(uint8_t page_number) const = 0;
-  virtual uint8_t GetControllerLeNumberOfSupportedAdverisingSets() const = 0;
-  virtual uint8_t GetControllerLocalExtendedFeaturesMaxPageNumber() const = 0;
-  virtual ~IController() {}
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/ihci_layer.h b/gd/shim/ihci_layer.h
deleted file mode 100644
index 5bb8e8f..0000000
--- a/gd/shim/ihci_layer.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-/**
- * Legacy interface and API into the Gd shim hci layer module.
- */
-using CommandCompleteCallback =
-    std::function<void(uint16_t command_op_code, std::vector<const uint8_t> data, const void* token)>;
-using CommandStatusCallback =
-    std::function<void(uint16_t command_op_code, std::vector<const uint8_t> data, const void* token, uint8_t status)>;
-
-namespace bluetooth {
-namespace shim {
-
-struct IHciLayer {
-  virtual void TransmitCommand(uint16_t op_code, const uint8_t* data, size_t len, const void* token) = 0;
-
-  virtual void RegisterCommandComplete(CommandCompleteCallback callback) = 0;
-  virtual void UnregisterCommandComplete() = 0;
-
-  virtual void RegisterCommandStatus(CommandStatusCallback callback) = 0;
-  virtual void UnregisterCommandStatus() = 0;
-
-  virtual ~IHciLayer() {}
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/iinquiry.h b/gd/shim/iinquiry.h
deleted file mode 100644
index 97fd1b1..0000000
--- a/gd/shim/iinquiry.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <cstdint>
-#include <functional>
-#include <vector>
-
-/**
- * The gd API exported to the legacy api
- */
-namespace bluetooth {
-namespace shim {
-
-using InquiryResultCallback = std::function<void(std::vector<const uint8_t> data)>;
-using InquiryResultWithRssiCallback = std::function<void(std::vector<const uint8_t> data)>;
-using ExtendedInquiryResultCallback = std::function<void(std::vector<const uint8_t> data)>;
-using InquiryCompleteCallback = std::function<void(uint16_t status)>;
-using InquiryCancelCompleteCallback = std::function<void(uint8_t mode)>;
-
-struct IInquiry {
-  virtual void StartGeneralInquiry(uint8_t duration, uint8_t max_responses) = 0;
-  virtual void StartLimitedInquiry(uint8_t duration, uint8_t max_responses) = 0;
-  virtual void StopInquiry() = 0;
-  virtual bool IsGeneralInquiryActive() const = 0;
-  virtual bool IsLimitedInquiryActive() const = 0;
-
-  virtual void StartGeneralPeriodicInquiry(uint8_t duration, uint8_t max_responses, uint16_t max_delay,
-                                           uint16_t min_delay) = 0;
-  virtual void StartLimitedPeriodicInquiry(uint8_t duration, uint8_t max_responses, uint16_t max_delay,
-                                           uint16_t min_delay) = 0;
-  virtual void StopPeriodicInquiry() = 0;
-  virtual bool IsGeneralPeriodicInquiryActive() const = 0;
-  virtual bool IsLimitedPeriodicInquiryActive() const = 0;
-
-  virtual void SetInterlacedScan() = 0;
-  virtual void SetStandardScan() = 0;
-
-  virtual void SetScanActivity(uint16_t interval, uint16_t window) = 0;
-  virtual void GetScanActivity(uint16_t& interval, uint16_t& window) const = 0;
-
-  virtual void SetStandardInquiryResultMode() = 0;
-  virtual void SetInquiryWithRssiResultMode() = 0;
-  virtual void SetExtendedInquiryResultMode() = 0;
-
-  virtual void RegisterInquiryResult(InquiryResultCallback callback) = 0;
-  virtual void UnregisterInquiryResult() = 0;
-  virtual void RegisterInquiryResultWithRssi(InquiryResultWithRssiCallback callback) = 0;
-  virtual void UnregisterInquiryResultWithRssi() = 0;
-  virtual void RegisterExtendedInquiryResult(ExtendedInquiryResultCallback callback) = 0;
-  virtual void UnregisterExtendedInquiryResult() = 0;
-  virtual void RegisterInquiryComplete(InquiryCompleteCallback callback) = 0;
-  virtual void UnregisterInquiryComplete() = 0;
-  virtual void RegisterInquiryCancelComplete(InquiryCancelCompleteCallback callback) = 0;
-  virtual void UnregisterInquiryCancelComplete() = 0;
-
-  virtual ~IInquiry() {}
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/il2cap.h b/gd/shim/il2cap.h
deleted file mode 100644
index 994953e..0000000
--- a/gd/shim/il2cap.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <cstdint>
-#include <functional>
-#include <future>
-#include <string>
-#include <vector>
-
-/**
- * The gd API exported to the legacy api
- */
-namespace bluetooth {
-namespace shim {
-
-using ConnectionClosedCallback = std::function<void(uint16_t cid, int error_code)>;
-using ConnectionOpenCallback = std::function<void(std::string string_address, uint16_t psm, uint16_t cid)>;
-using ReadDataReadyCallback = std::function<void(uint16_t cid, std::vector<const uint8_t> data)>;
-
-struct IL2cap {
-  virtual void RegisterService(uint16_t psm, ConnectionOpenCallback on_open, std::promise<void> completed) = 0;
-  virtual void UnregisterService(uint16_t psm) = 0;
-
-  virtual void CreateConnection(uint16_t psm, const std::string address, ConnectionOpenCallback on_open,
-                                std::promise<uint16_t> completed) = 0;
-  virtual void CloseConnection(uint16_t cid) = 0;
-
-  virtual void SetReadDataReadyCallback(uint16_t cid, ReadDataReadyCallback on_data_ready) = 0;
-  virtual void SetConnectionClosedCallback(uint16_t cid, ConnectionClosedCallback on_closed) = 0;
-
-  virtual void Write(uint16_t cid, const uint8_t* data, size_t len) = 0;
-  virtual void WriteFlushable(uint16_t cid, const uint8_t* data, size_t len) = 0;
-  virtual void WriteNonFlushable(uint16_t cid, const uint8_t* data, size_t len) = 0;
-
-  virtual void SendLoopbackResponse(std::function<void()>) = 0;
-  virtual ~IL2cap() {}
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/iname.h b/gd/shim/iname.h
deleted file mode 100644
index c99ba37..0000000
--- a/gd/shim/iname.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <cstdint>
-#include <functional>
-#include <string>
-
-/**
- * The gd API exported to the legacy api
- */
-using ReadRemoteNameCallback =
-    std::function<void(std::string address_string, uint8_t hci_status, std::array<uint8_t, 248> remote_name)>;
-using CancelRemoteNameCallback = std::function<void(std::string address_string, uint8_t hci_status)>;
-
-namespace bluetooth {
-namespace shim {
-
-struct IName {
-  virtual void ReadRemoteNameRequest(std::string remote_address, ReadRemoteNameCallback callback) = 0;
-  virtual void CancelRemoteNameRequest(std::string remote_address, CancelRemoteNameCallback callback) = 0;
-
-  virtual ~IName() {}
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/inquiry.cc b/gd/shim/inquiry.cc
deleted file mode 100644
index 20eb155..0000000
--- a/gd/shim/inquiry.cc
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "bt_gd_shim"
-
-#include <functional>
-#include <memory>
-
-#include "common/bidi_queue.h"
-#include "hci/address.h"
-#include "hci/controller.h"
-#include "hci/hci_packets.h"
-#include "module.h"
-#include "neighbor/inquiry.h"
-#include "neighbor/scan_parameters.h"
-#include "os/handler.h"
-#include "os/log.h"
-#include "shim/inquiry.h"
-
-namespace bluetooth {
-namespace shim {
-
-struct Inquiry::impl {
-  void Result(hci::InquiryResultView view);
-  void ResultWithRssi(hci::InquiryResultWithRssiView view);
-  void ExtendedResult(hci::ExtendedInquiryResultView view);
-  void Complete(hci::ErrorCode status);
-
-  void RegisterInquiryResult(InquiryResultCallback callback);
-  void UnregisterInquiryResult();
-  void RegisterInquiryResultWithRssi(InquiryResultWithRssiCallback callback);
-  void UnregisterInquiryResultWithRssi();
-  void RegisterExtendedInquiryResult(ExtendedInquiryResultCallback callback);
-  void UnregisterExtendedInquiryResult();
-  void RegisterInquiryComplete(InquiryCompleteCallback callback);
-  void UnregisterInquiryComplete();
-  void RegisterInquiryCancelComplete(InquiryCancelCompleteCallback callback);
-  void UnregisterInquiryCancelComplete();
-
-  InquiryResultCallback shim_result_callback_;
-  InquiryResultWithRssiCallback shim_result_with_rssi_callback_;
-  ExtendedInquiryResultCallback shim_extended_result_callback_;
-  InquiryCompleteCallback shim_complete_callback_;
-  InquiryCancelCompleteCallback shim_cancel_complete_callback_;
-
-  neighbor::InquiryModule* module_{nullptr};
-
-  impl(neighbor::InquiryModule* module);
-  ~impl();
-
-  neighbor::ScanParameters params_{
-      .interval = static_cast<neighbor::ScanInterval>(0),
-      .window = static_cast<neighbor::ScanWindow>(0),
-  };
-  bool general_inquiry_active_{false};
-  bool limited_inquiry_active_{false};
-  bool general_periodic_inquiry_active_{false};
-  bool limited_periodic_inquiry_active_{false};
-};
-
-const ModuleFactory Inquiry::Factory = ModuleFactory([]() { return new Inquiry(); });
-
-void Inquiry::impl::Result(hci::InquiryResultView view) {
-  ASSERT(view.size() >= sizeof(uint16_t));
-  ASSERT(shim_result_callback_ != nullptr);
-  std::vector<const uint8_t> v(view.begin() + sizeof(uint16_t), view.end());
-  shim_result_callback_(v);
-}
-
-void Inquiry::impl::ResultWithRssi(hci::InquiryResultWithRssiView view) {
-  ASSERT(view.size() >= sizeof(uint16_t));
-  ASSERT(shim_result_with_rssi_callback_ != nullptr);
-  std::vector<const uint8_t> v(view.begin() + sizeof(uint16_t), view.end());
-  shim_result_with_rssi_callback_(v);
-}
-
-void Inquiry::impl::ExtendedResult(hci::ExtendedInquiryResultView view) {
-  ASSERT(view.size() >= sizeof(uint16_t));
-  ASSERT(shim_extended_result_callback_ != nullptr);
-  std::vector<const uint8_t> v(view.begin() + sizeof(uint16_t), view.end());
-  shim_extended_result_callback_(v);
-}
-
-void Inquiry::impl::Complete(hci::ErrorCode status) {
-  ASSERT(shim_complete_callback_ != nullptr);
-  shim_complete_callback_(static_cast<uint16_t>(status));
-}
-
-void Inquiry::impl::RegisterInquiryResult(shim::InquiryResultCallback callback) {
-  if (shim_result_callback_ != nullptr) {
-    LOG_WARN("Registering inquiry result without unregistering");
-  }
-  shim_result_callback_ = callback;
-}
-
-void Inquiry::impl::UnregisterInquiryResult() {
-  if (shim_result_callback_ == nullptr) {
-    LOG_WARN("Unregistering inquiry result without registering");
-  }
-  shim_result_callback_ = nullptr;
-}
-
-void Inquiry::impl::RegisterInquiryResultWithRssi(shim::InquiryResultWithRssiCallback callback) {
-  if (shim_result_with_rssi_callback_ != nullptr) {
-    LOG_WARN("Registering inquiry result with rssi without unregistering");
-  }
-  shim_result_with_rssi_callback_ = callback;
-}
-
-void Inquiry::impl::UnregisterInquiryResultWithRssi() {
-  if (shim_result_with_rssi_callback_ == nullptr) {
-    LOG_WARN("Unregistering inquiry result with rssi without registering");
-  }
-  shim_result_with_rssi_callback_ = nullptr;
-}
-
-void Inquiry::impl::RegisterExtendedInquiryResult(shim::ExtendedInquiryResultCallback callback) {
-  if (shim_result_with_rssi_callback_ != nullptr) {
-    LOG_WARN("Registering extended inquiry result without unregistering");
-  }
-  shim_extended_result_callback_ = callback;
-}
-
-void Inquiry::impl::UnregisterExtendedInquiryResult() {
-  if (shim_extended_result_callback_ == nullptr) {
-    LOG_WARN("Unregistering extended inquiry result without registering");
-  }
-  shim_extended_result_callback_ = nullptr;
-}
-
-void Inquiry::impl::RegisterInquiryComplete(shim::InquiryCompleteCallback callback) {
-  if (shim_result_with_rssi_callback_ != nullptr) {
-    LOG_WARN("Registering inquiry complete without unregistering");
-  }
-  shim_complete_callback_ = callback;
-}
-
-void Inquiry::impl::UnregisterInquiryComplete() {
-  if (shim_result_with_rssi_callback_ == nullptr) {
-    LOG_WARN("Unregistering inquiry complete without registering");
-  }
-  shim_complete_callback_ = nullptr;
-}
-
-void Inquiry::impl::RegisterInquiryCancelComplete(shim::InquiryCancelCompleteCallback callback) {
-  if (shim_cancel_complete_callback_ != nullptr) {
-    LOG_WARN("Registering inquiry cancel complete without unregistering");
-  }
-  shim_cancel_complete_callback_ = callback;
-}
-
-void Inquiry::impl::UnregisterInquiryCancelComplete() {
-  if (shim_cancel_complete_callback_ == nullptr) {
-    LOG_WARN("Unregistering inquiry cancel complete without registering");
-  }
-  shim_cancel_complete_callback_ = nullptr;
-}
-
-Inquiry::impl::impl(neighbor::InquiryModule* inquiry_module) : module_(inquiry_module) {
-  neighbor::InquiryCallbacks inquiry_callbacks;
-  inquiry_callbacks.result = std::bind(&Inquiry::impl::Result, this, std::placeholders::_1);
-  inquiry_callbacks.result_with_rssi = std::bind(&Inquiry::impl::ResultWithRssi, this, std::placeholders::_1);
-  inquiry_callbacks.extended_result = std::bind(&Inquiry::impl::ExtendedResult, this, std::placeholders::_1);
-  inquiry_callbacks.complete = std::bind(&Inquiry::impl::Complete, this, std::placeholders::_1);
-
-  module_->RegisterCallbacks(inquiry_callbacks);
-}
-
-Inquiry::impl::~impl() {
-  module_->UnregisterCallbacks();
-}
-
-void Inquiry::StartGeneralInquiry(uint8_t inquiry_length, uint8_t num_responses) {
-  pimpl_->general_inquiry_active_ = true;
-  return pimpl_->module_->StartGeneralInquiry(inquiry_length, num_responses);
-}
-
-void Inquiry::StartLimitedInquiry(uint8_t inquiry_length, uint8_t num_responses) {
-  pimpl_->limited_inquiry_active_ = true;
-  return pimpl_->module_->StartLimitedInquiry(inquiry_length, num_responses);
-}
-
-void Inquiry::StopInquiry() {
-  pimpl_->limited_inquiry_active_ = false;
-  pimpl_->general_inquiry_active_ = false;
-  return pimpl_->module_->StopInquiry();
-}
-
-bool Inquiry::IsGeneralInquiryActive() const {
-  return pimpl_->general_inquiry_active_;
-}
-
-bool Inquiry::IsLimitedInquiryActive() const {
-  return pimpl_->limited_inquiry_active_;
-}
-
-void Inquiry::StartGeneralPeriodicInquiry(uint8_t inquiry_length, uint8_t num_responses, uint16_t max_delay,
-                                          uint16_t min_delay) {
-  pimpl_->general_periodic_inquiry_active_ = true;
-  return pimpl_->module_->StartGeneralPeriodicInquiry(inquiry_length, num_responses, max_delay, min_delay);
-}
-
-void Inquiry::StartLimitedPeriodicInquiry(uint8_t inquiry_length, uint8_t num_responses, uint16_t max_delay,
-                                          uint16_t min_delay) {
-  pimpl_->limited_periodic_inquiry_active_ = true;
-  return pimpl_->module_->StartLimitedPeriodicInquiry(inquiry_length, num_responses, max_delay, min_delay);
-}
-
-void Inquiry::StopPeriodicInquiry() {
-  pimpl_->limited_periodic_inquiry_active_ = false;
-  pimpl_->general_periodic_inquiry_active_ = false;
-  return pimpl_->module_->StopPeriodicInquiry();
-}
-
-bool Inquiry::IsGeneralPeriodicInquiryActive() const {
-  return pimpl_->general_periodic_inquiry_active_;
-}
-
-bool Inquiry::IsLimitedPeriodicInquiryActive() const {
-  return pimpl_->limited_periodic_inquiry_active_;
-}
-
-void Inquiry::SetInterlacedScan() {
-  pimpl_->module_->SetInterlacedScan();
-}
-
-void Inquiry::SetStandardScan() {
-  pimpl_->module_->SetStandardScan();
-}
-
-void Inquiry::SetScanActivity(uint16_t interval, uint16_t window) {
-  pimpl_->params_.interval = interval;
-  pimpl_->params_.window = window;
-  pimpl_->module_->SetScanActivity(pimpl_->params_);
-}
-
-void Inquiry::GetScanActivity(uint16_t& interval, uint16_t& window) const {
-  interval = static_cast<uint16_t>(pimpl_->params_.interval);
-  window = static_cast<uint16_t>(pimpl_->params_.window);
-}
-
-void Inquiry::SetStandardInquiryResultMode() {
-  pimpl_->module_->SetStandardInquiryResultMode();
-}
-
-void Inquiry::SetInquiryWithRssiResultMode() {
-  pimpl_->module_->SetInquiryWithRssiResultMode();
-}
-
-void Inquiry::SetExtendedInquiryResultMode() {
-  pimpl_->module_->SetExtendedInquiryResultMode();
-}
-
-void Inquiry::RegisterInquiryResult(shim::InquiryResultCallback callback) {
-  pimpl_->RegisterInquiryResult(callback);
-}
-
-void Inquiry::UnregisterInquiryResult() {
-  pimpl_->UnregisterInquiryResult();
-}
-
-void Inquiry::RegisterInquiryResultWithRssi(shim::InquiryResultWithRssiCallback callback) {
-  pimpl_->RegisterInquiryResultWithRssi(callback);
-}
-
-void Inquiry::UnregisterInquiryResultWithRssi() {
-  pimpl_->UnregisterInquiryResultWithRssi();
-}
-
-void Inquiry::RegisterExtendedInquiryResult(shim::ExtendedInquiryResultCallback callback) {
-  pimpl_->RegisterExtendedInquiryResult(callback);
-}
-
-void Inquiry::UnregisterExtendedInquiryResult() {
-  pimpl_->UnregisterExtendedInquiryResult();
-}
-
-void Inquiry::RegisterInquiryComplete(InquiryCompleteCallback callback) {
-  pimpl_->RegisterInquiryComplete(callback);
-}
-
-void Inquiry::UnregisterInquiryComplete() {
-  pimpl_->UnregisterInquiryComplete();
-}
-
-void Inquiry::RegisterInquiryCancelComplete(InquiryCancelCompleteCallback callback) {
-  pimpl_->RegisterInquiryCancelComplete(callback);
-}
-
-void Inquiry::UnregisterInquiryCancelComplete() {
-  pimpl_->UnregisterInquiryCancelComplete();
-}
-
-/**
- * Module methods
- */
-void Inquiry::ListDependencies(ModuleList* list) {
-  list->add<neighbor::InquiryModule>();
-}
-
-void Inquiry::Start() {
-  pimpl_ = std::make_unique<impl>(GetDependency<neighbor::InquiryModule>());
-}
-
-void Inquiry::Stop() {
-  pimpl_.reset();
-}
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/inquiry.h b/gd/shim/inquiry.h
deleted file mode 100644
index 9051d70..0000000
--- a/gd/shim/inquiry.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <memory>
-#include <string>
-
-#include "module.h"
-#include "shim/iinquiry.h"
-
-namespace bluetooth {
-namespace shim {
-
-class Inquiry : public bluetooth::Module, public bluetooth::shim::IInquiry {
- public:
-  void StartGeneralInquiry(uint8_t duration, uint8_t max_responses) override;
-  void StartLimitedInquiry(uint8_t duration, uint8_t max_responses) override;
-  void StopInquiry() override;
-  bool IsGeneralInquiryActive() const override;
-  bool IsLimitedInquiryActive() const override;
-
-  void StartGeneralPeriodicInquiry(uint8_t duration, uint8_t max_responses, uint16_t max_delay,
-                                   uint16_t min_delay) override;
-  void StartLimitedPeriodicInquiry(uint8_t duration, uint8_t max_responses, uint16_t max_delay,
-                                   uint16_t min_delay) override;
-  void StopPeriodicInquiry() override;
-  bool IsGeneralPeriodicInquiryActive() const override;
-  bool IsLimitedPeriodicInquiryActive() const override;
-
-  void SetInterlacedScan() override;
-  void SetStandardScan() override;
-
-  void SetScanActivity(uint16_t interval, uint16_t window) override;
-  void GetScanActivity(uint16_t& interval, uint16_t& window) const override;
-
-  void SetStandardInquiryResultMode() override;
-  void SetInquiryWithRssiResultMode() override;
-  void SetExtendedInquiryResultMode() override;
-
-  void RegisterInquiryResult(InquiryResultCallback callback) override;
-  void UnregisterInquiryResult() override;
-  void RegisterInquiryResultWithRssi(InquiryResultWithRssiCallback callback) override;
-  void UnregisterInquiryResultWithRssi() override;
-  void RegisterExtendedInquiryResult(ExtendedInquiryResultCallback callback) override;
-  void UnregisterExtendedInquiryResult() override;
-  void RegisterInquiryComplete(InquiryCompleteCallback callback) override;
-  void UnregisterInquiryComplete() override;
-  void RegisterInquiryCancelComplete(InquiryCancelCompleteCallback callback) override;
-  void UnregisterInquiryCancelComplete() override;
-
-  Inquiry() = default;
-  ~Inquiry() = default;
-
-  static const ModuleFactory Factory;
-
- protected:
-  void ListDependencies(ModuleList* list) override;  // Module
-  void Start() override;                             // Module
-  void Stop() override;                              // Module
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-  DISALLOW_COPY_AND_ASSIGN(Inquiry);
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/ipage.h b/gd/shim/ipage.h
deleted file mode 100644
index 28c2762..0000000
--- a/gd/shim/ipage.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <cstdint>
-
-/**
- * The gd API exported to the legacy api
- */
-namespace bluetooth {
-namespace shim {
-
-struct IPage {
-  virtual void SetScanActivity(uint16_t interval, uint16_t window) = 0;
-  virtual void GetScanActivity(uint16_t& interval, uint16_t& window) const = 0;
-
-  virtual void SetInterlacedScan() = 0;
-  virtual void SetStandardScan() = 0;
-
-  virtual ~IPage() {}
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/iscanning.h b/gd/shim/iscanning.h
deleted file mode 100644
index f96f372..0000000
--- a/gd/shim/iscanning.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <cstdint>
-#include <functional>
-
-/**
- * The gd API exported to the legacy api
- */
-namespace bluetooth {
-namespace shim {
-
-struct AdvertisingReport {
-  uint16_t extended_event_type;
-  std::string string_address;
-  uint8_t address_type;
-  int8_t rssi;
-  uint8_t* data;
-  size_t len;
-};
-
-struct DirectedAdvertisingReport : public AdvertisingReport {
-  DirectedAdvertisingReport(AdvertisingReport report) : AdvertisingReport(report) {}
-  uint8_t directed_advertising_type;
-};
-
-struct ExtendedAdvertisingReport : public DirectedAdvertisingReport {
-  ExtendedAdvertisingReport(AdvertisingReport report) : DirectedAdvertisingReport(report) {}
-};
-
-using AdvertisingReportCallback = std::function<void(AdvertisingReport report)>;
-using DirectedAdvertisingReportCallback = std::function<void(DirectedAdvertisingReport report)>;
-using ExtendedAdvertisingReportCallback = std::function<void(ExtendedAdvertisingReport report)>;
-using ScanningTimeoutCallback = std::function<void()>;
-
-struct IScanning {
-  virtual void StartScanning(bool set_active, AdvertisingReportCallback advertising_callback,
-                             DirectedAdvertisingReportCallback directed_advertising_callback,
-                             ExtendedAdvertisingReportCallback extended_advertising_callback,
-                             ScanningTimeoutCallback timeout_callback) = 0;
-  virtual void StopScanning() = 0;
-
-  virtual ~IScanning() {}
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/istack.h b/gd/shim/istack.h
deleted file mode 100644
index f432dac..0000000
--- a/gd/shim/istack.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-/**
- * Legacy stack manipulation methods to allow the legacy stack to start
- * and stop the stack, and to provide Gd shim stack module API access.
- */
-namespace bluetooth {
-namespace shim {
-
-struct IAdvertising;
-struct IController;
-struct IConnectability;
-struct IDiscoverability;
-struct IHciLayer;
-struct IInquiry;
-struct IName;
-struct IL2cap;
-struct IPage;
-struct IScanning;
-
-struct IStack {
-  virtual void Start() = 0;
-  virtual void Stop() = 0;
-
-  virtual IAdvertising* GetAdvertising() = 0;
-  virtual IController* GetController() = 0;
-  virtual IConnectability* GetConnectability() = 0;
-  virtual IDiscoverability* GetDiscoverability() = 0;
-  virtual IHciLayer* GetHciLayer() = 0;
-  virtual IInquiry* GetInquiry() = 0;
-  virtual IName* GetName() = 0;
-  virtual IL2cap* GetL2cap() = 0;
-  virtual IPage* GetPage() = 0;
-  virtual IScanning* GetScanning() = 0;
-
-  virtual ~IStack() {}
-};
-
-IStack* GetGabeldorscheStack();
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/l2cap.cc b/gd/shim/l2cap.cc
index d703972..9df1528 100644
--- a/gd/shim/l2cap.cc
+++ b/gd/shim/l2cap.cc
@@ -15,12 +15,14 @@
  */
 #define LOG_TAG "bt_gd_shim"
 
+#include "shim/l2cap.h"
+
 #include <cstdint>
 #include <functional>
-#include <future>
 #include <memory>
-#include <mutex>
 #include <queue>
+#include <set>
+#include <string>
 #include <unordered_map>
 #include <vector>
 
@@ -29,29 +31,39 @@
 #include "hci/hci_packets.h"
 #include "l2cap/classic/dynamic_channel_manager.h"
 #include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/classic/security_policy.h"
 #include "l2cap/psm.h"
-#include "l2cap/security_policy.h"
 #include "module.h"
 #include "os/handler.h"
 #include "os/log.h"
 #include "packet/packet_view.h"
 #include "packet/raw_builder.h"
-#include "shim/l2cap.h"
+#include "shim/dumpsys.h"
 
 namespace bluetooth {
 namespace shim {
 
-const ModuleFactory L2cap::Factory = ModuleFactory([]() { return new L2cap(); });
+namespace {
+
+constexpr char kModuleName[] = "shim::L2cap";
+
+constexpr bool kConnectionFailed = false;
+constexpr bool kConnectionOpened = true;
 
 using ConnectionInterfaceDescriptor = uint16_t;
-static const ConnectionInterfaceDescriptor kInvalidConnectionInterfaceDescriptor = 0;
-static const ConnectionInterfaceDescriptor kStartConnectionInterfaceDescriptor = 64;
-static const ConnectionInterfaceDescriptor kMaxConnections = UINT16_MAX - kStartConnectionInterfaceDescriptor - 1;
+constexpr ConnectionInterfaceDescriptor kInvalidConnectionInterfaceDescriptor = 0;
+constexpr ConnectionInterfaceDescriptor kStartConnectionInterfaceDescriptor = 64;
+constexpr ConnectionInterfaceDescriptor kMaxConnections = UINT16_MAX - kStartConnectionInterfaceDescriptor - 1;
 
-using ServiceInterfaceCallback =
-    std::function<void(l2cap::Psm psm, l2cap::classic::DynamicChannelManager::RegistrationResult result)>;
-using ConnectionInterfaceCallback =
-    std::function<void(l2cap::Psm psm, std::unique_ptr<l2cap::classic::DynamicChannel>)>;
+using PendingConnectionId = int;
+
+using ConnectionClosed = std::function<void(ConnectionInterfaceDescriptor)>;
+using PendingConnectionOpen = std::function<void(std::unique_ptr<l2cap::classic::DynamicChannel>)>;
+using PendingConnectionFail = std::function<void(l2cap::classic::DynamicChannelManager::ConnectionResult)>;
+using RegisterServiceComplete = std::function<void(l2cap::Psm, bool is_registered)>;
+using UnregisterServiceDone = std::function<void()>;
+using ServiceConnectionOpen =
+    std::function<void(ConnectionCompleteCallback, std::unique_ptr<l2cap::classic::DynamicChannel>)>;
 
 std::unique_ptr<packet::RawBuilder> MakeUniquePacket(const uint8_t* data, size_t len) {
   packet::RawBuilder builder;
@@ -61,14 +73,23 @@
   return payload;
 }
 
+}  // namespace
+
 class ConnectionInterface {
  public:
-  ConnectionInterface(ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::classic::DynamicChannel> channel,
-                      os::Handler* handler)
-      : cid_(cid), channel_(std::move(channel)), handler_(handler), on_data_ready_callback_(nullptr),
-        on_connection_closed_callback_(nullptr), address_(channel_->GetDevice()) {
-    channel_->RegisterOnCloseCallback(
-        handler_, common::BindOnce(&ConnectionInterface::OnConnectionClosed, common::Unretained(this)));
+  ConnectionInterface(
+      ConnectionInterfaceDescriptor cid,
+      std::unique_ptr<l2cap::classic::DynamicChannel> channel,
+      os::Handler* handler,
+      ConnectionClosed on_closed)
+      : cid_(cid),
+        channel_(std::move(channel)),
+        handler_(handler),
+        on_data_ready_callback_(nullptr),
+        on_connection_closed_callback_(nullptr),
+        address_(channel_->GetDevice().GetAddress()),
+        on_closed_(on_closed) {
+    channel_->RegisterOnCloseCallback(handler_->BindOnceOn(this, &ConnectionInterface::OnConnectionClosed));
     channel_->GetQueueUpEnd()->RegisterDequeue(
         handler_, common::Bind(&ConnectionInterface::OnReadReady, common::Unretained(this)));
     dequeue_registered_ = true;
@@ -125,10 +146,18 @@
   }
 
   void OnConnectionClosed(hci::ErrorCode error_code) {
-    LOG_DEBUG("Channel interface closed reason:%s cid:%hd device:%s", hci::ErrorCodeText(error_code).c_str(), cid_,
-              address_.ToString().c_str());
+    LOG_DEBUG(
+        "Channel interface closed reason:%s cid:%hd device:%s",
+        hci::ErrorCodeText(error_code).c_str(),
+        cid_,
+        address_.ToString().c_str());
+    if (dequeue_registered_) {
+      channel_->GetQueueUpEnd()->UnregisterDequeue();
+      dequeue_registered_ = false;
+    }
     ASSERT(on_connection_closed_callback_ != nullptr);
     on_connection_closed_callback_(cid_, static_cast<int>(error_code));
+    on_closed_(cid_);
   }
 
   void SetConnectionClosedCallback(::bluetooth::shim::ConnectionClosedCallback on_connection_closed) {
@@ -137,6 +166,10 @@
     on_connection_closed_callback_ = std::move(on_connection_closed);
   }
 
+  hci::Address GetRemoteAddress() const {
+    return address_;
+  }
+
  private:
   const ConnectionInterfaceDescriptor cid_;
   const std::unique_ptr<l2cap::classic::DynamicChannel> channel_;
@@ -147,15 +180,19 @@
 
   const hci::Address address_;
 
+  ConnectionClosed on_closed_{};
+
   std::queue<std::unique_ptr<packet::PacketBuilder<hci::kLittleEndian>>> write_queue_;
 
   bool enqueue_registered_{false};
   bool dequeue_registered_{false};
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionInterface);
 };
 
-struct ConnectionInterfaceManager {
+class ConnectionInterfaceManager {
  public:
-  ConnectionInterfaceDescriptor AddChannel(std::unique_ptr<l2cap::classic::DynamicChannel> channel);
+  void AddConnection(ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::classic::DynamicChannel> channel);
   void RemoveConnection(ConnectionInterfaceDescriptor cid);
 
   void SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready);
@@ -167,93 +204,120 @@
     return cid_to_interface_map_.size();
   }
 
-  void GeneralCallback(ConnectionOpenCallback on_open, hci::Address address, l2cap::Psm psm,
-                       ConnectionInterfaceDescriptor cid) {
-    on_open(address.ToString(), static_cast<uint16_t>(psm), static_cast<uint16_t>(cid));
+  void ConnectionOpened(
+      ConnectionCompleteCallback on_complete,
+      l2cap::Psm psm,
+      ConnectionInterfaceDescriptor cid,
+      l2cap::Cid remote_cid) {
+    hci::Address address = cid_to_interface_map_[cid]->GetRemoteAddress();
+    LOG_DEBUG("Connection opened address:%s psm:%hd cid:%hd", address.ToString().c_str(), psm, cid);
+    on_complete(
+        address.ToString(),
+        static_cast<uint16_t>(psm),
+        static_cast<uint16_t>(cid),
+        static_cast<uint16_t>(remote_cid),
+        kConnectionOpened);
   }
 
-  void ConnectionOpened(ConnectionOpenCallback on_open, hci::Address address, l2cap::Psm psm,
-                        ConnectionInterfaceDescriptor cid) {
-    LOG_DEBUG("%s address:%s psm:%hd cid:%hd", __func__, address.ToString().c_str(), psm, cid);
-    handler_->Post(common::BindOnce(&ConnectionInterfaceManager::GeneralCallback, common::Unretained(this), on_open,
-                                    address, psm, cid));
-    // TODO(cmanton) queue this pending connection address/psm tuple up for deletion
-    // There may be multiple, so only remove one
-  }
-
-  void ConnectionFailed(hci::Address address, l2cap::Psm psm) {
-    LOG_DEBUG("%s Connection Failed", __func__);
-    // TODO(cmanton) queue this pending connection address/psm tuple up for deletion
-    // There may be multiple, so only remove one
+  void ConnectionFailed(
+      ConnectionCompleteCallback on_complete, hci::Address address, l2cap::Psm psm, ConnectionInterfaceDescriptor cid) {
+    LOG_DEBUG("Connection failed address:%s psm:%hd", address.ToString().c_str(), psm);
+    on_complete(address.ToString(), static_cast<uint16_t>(psm), static_cast<uint16_t>(cid), 0, kConnectionFailed);
   }
 
   ConnectionInterfaceManager(os::Handler* handler);
 
+  ConnectionInterfaceDescriptor AllocateConnectionInterfaceDescriptor();
+  void FreeConnectionInterfaceDescriptor(ConnectionInterfaceDescriptor cid);
+
  private:
   os::Handler* handler_;
   ConnectionInterfaceDescriptor current_connection_interface_descriptor_;
 
   bool HasResources() const;
-  bool Exists(ConnectionInterfaceDescriptor id) const;
+  bool ConnectionExists(ConnectionInterfaceDescriptor id) const;
+  bool CidExists(ConnectionInterfaceDescriptor id) const;
+  void ConnectionClosed(ConnectionInterfaceDescriptor cid, std::unique_ptr<ConnectionInterface> connection);
 
   std::unordered_map<ConnectionInterfaceDescriptor, std::unique_ptr<ConnectionInterface>> cid_to_interface_map_;
-  ConnectionInterfaceDescriptor AllocateConnectionInterfaceDescriptor();
+  std::set<ConnectionInterfaceDescriptor> active_cid_set_;
+
   ConnectionInterfaceManager() = delete;
 };
 
 ConnectionInterfaceManager::ConnectionInterfaceManager(os::Handler* handler)
     : handler_(handler), current_connection_interface_descriptor_(kStartConnectionInterfaceDescriptor) {}
 
-bool ConnectionInterfaceManager::Exists(ConnectionInterfaceDescriptor cid) const {
+bool ConnectionInterfaceManager::ConnectionExists(ConnectionInterfaceDescriptor cid) const {
   return cid_to_interface_map_.find(cid) != cid_to_interface_map_.end();
 }
 
+bool ConnectionInterfaceManager::CidExists(ConnectionInterfaceDescriptor cid) const {
+  return active_cid_set_.find(cid) != active_cid_set_.end();
+}
+
 ConnectionInterfaceDescriptor ConnectionInterfaceManager::AllocateConnectionInterfaceDescriptor() {
   ASSERT(HasResources());
-  while (Exists(current_connection_interface_descriptor_)) {
+  while (CidExists(current_connection_interface_descriptor_)) {
     if (++current_connection_interface_descriptor_ == kInvalidConnectionInterfaceDescriptor) {
       current_connection_interface_descriptor_ = kStartConnectionInterfaceDescriptor;
     }
   }
+  active_cid_set_.insert(current_connection_interface_descriptor_);
   return current_connection_interface_descriptor_++;
 }
 
-ConnectionInterfaceDescriptor ConnectionInterfaceManager::AddChannel(
-    std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
-  if (!HasResources()) {
-    return kInvalidConnectionInterfaceDescriptor;
-  }
-  ConnectionInterfaceDescriptor cid = AllocateConnectionInterfaceDescriptor();
+void ConnectionInterfaceManager::FreeConnectionInterfaceDescriptor(ConnectionInterfaceDescriptor cid) {
+  ASSERT(CidExists(cid));
+  active_cid_set_.erase(cid);
+}
 
-  auto channel_interface = std::make_unique<ConnectionInterface>(cid, std::move(channel), handler_);
-  cid_to_interface_map_[cid] = std::move(channel_interface);
-  return cid;
+void ConnectionInterfaceManager::ConnectionClosed(
+    ConnectionInterfaceDescriptor cid, std::unique_ptr<ConnectionInterface> connection) {
+  cid_to_interface_map_.erase(cid);
+  FreeConnectionInterfaceDescriptor(cid);
+}
+
+void ConnectionInterfaceManager::AddConnection(
+    ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+  ASSERT(cid_to_interface_map_.count(cid) == 0);
+  cid_to_interface_map_.emplace(
+      cid,
+      std::make_unique<ConnectionInterface>(
+          cid, std::move(channel), handler_, [this](ConnectionInterfaceDescriptor cid) {
+            LOG_DEBUG("Deleting connection interface cid:%hd", cid);
+            auto connection = std::move(cid_to_interface_map_.at(cid));
+            handler_->Post(common::BindOnce(
+                &ConnectionInterfaceManager::ConnectionClosed, common::Unretained(this), cid, std::move(connection)));
+          }));
 }
 
 void ConnectionInterfaceManager::RemoveConnection(ConnectionInterfaceDescriptor cid) {
-  ASSERT(cid_to_interface_map_.count(cid) == 1);
-  cid_to_interface_map_.find(cid)->second->Close();
-  cid_to_interface_map_.erase(cid);
+  if (cid_to_interface_map_.count(cid) == 1) {
+    cid_to_interface_map_.find(cid)->second->Close();
+  } else {
+    LOG_WARN("Closing a pending connection cid:%hd", cid);
+  }
 }
 
 bool ConnectionInterfaceManager::HasResources() const {
   return cid_to_interface_map_.size() < kMaxConnections;
 }
 
-void ConnectionInterfaceManager::SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid,
-                                                          ReadDataReadyCallback on_data_ready) {
-  ASSERT(Exists(cid));
+void ConnectionInterfaceManager::SetReadDataReadyCallback(
+    ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready) {
+  ASSERT(ConnectionExists(cid));
   return cid_to_interface_map_[cid]->SetReadDataReadyCallback(on_data_ready);
 }
 
-void ConnectionInterfaceManager::SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid,
-                                                             ConnectionClosedCallback on_closed) {
-  ASSERT(Exists(cid));
+void ConnectionInterfaceManager::SetConnectionClosedCallback(
+    ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed) {
+  ASSERT(ConnectionExists(cid));
   return cid_to_interface_map_[cid]->SetConnectionClosedCallback(on_closed);
 }
 
 bool ConnectionInterfaceManager::Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet) {
-  if (!Exists(cid)) {
+  if (!ConnectionExists(cid)) {
     return false;
   }
   cid_to_interface_map_[cid]->Write(std::move(packet));
@@ -262,18 +326,25 @@
 
 class PendingConnection {
  public:
-  PendingConnection(ConnectionInterfaceManager* connection_interface_manager, l2cap::Psm psm, hci::Address address,
-                    ConnectionOpenCallback on_open, std::promise<uint16_t> completed)
-      : connection_interface_manager_(connection_interface_manager), psm_(psm), address_(address),
-        on_open_(std::move(on_open)), completed_(std::move(completed)) {}
+  PendingConnection(
+      ConnectionInterfaceDescriptor cid,
+      l2cap::Psm psm,
+      hci::Address address,
+      ConnectionCompleteCallback on_complete,
+      PendingConnectionOpen pending_open,
+      PendingConnectionFail pending_fail)
+      : cid_(cid),
+        psm_(psm),
+        address_(address),
+        on_complete_(std::move(on_complete)),
+        pending_open_(pending_open),
+        pending_fail_(pending_fail) {}
 
   void OnConnectionOpen(std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
     LOG_DEBUG("Local initiated connection is open to device:%s for psm:%hd", address_.ToString().c_str(), psm_);
-    ConnectionInterfaceDescriptor cid = connection_interface_manager_->AddChannel(std::move(channel));
-    completed_.set_value(cid);
-    // Attempt to avoid async race condition with upper stack
-    std::this_thread::yield();
-    connection_interface_manager_->ConnectionOpened(std::move(on_open_), address_, psm_, cid);
+    ASSERT_LOG(
+        address_ == channel->GetDevice().GetAddress(), " Expected remote device does not match actual remote device");
+    pending_open_(std::move(channel));
   }
 
   void OnConnectionFailure(l2cap::classic::DynamicChannelManager::ConnectionResult result) {
@@ -283,83 +354,57 @@
         LOG_WARN("Connection failed result:success hci:%s", hci::ErrorCodeText(result.hci_error).c_str());
         break;
       case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED:
-        LOG_DEBUG("Connection failed result:no service registered hci:%s",
-                  hci::ErrorCodeText(result.hci_error).c_str());
+        LOG_DEBUG(
+            "Connection failed result:no service registered hci:%s", hci::ErrorCodeText(result.hci_error).c_str());
         break;
       case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_HCI_ERROR:
         LOG_DEBUG("Connection failed result:hci error hci:%s", hci::ErrorCodeText(result.hci_error).c_str());
         break;
       case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR:
-        LOG_DEBUG("Connection failed result:l2cap error hci:%s l2cap:%s", hci::ErrorCodeText(result.hci_error).c_str(),
-                  l2cap::ConnectionResponseResultText(result.l2cap_connection_response_result).c_str());
+        LOG_DEBUG(
+            "Connection failed result:l2cap error hci:%s l2cap:%s",
+            hci::ErrorCodeText(result.hci_error).c_str(),
+            l2cap::ConnectionResponseResultText(result.l2cap_connection_response_result).c_str());
+        break;
+      case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_REMOTE_NOT_SUPPORT:
+        LOG_DEBUG("Connection failed result:Remote not support required retransmission and flow control mode");
+        break;
+      case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_SECURITY_BLOCK:
+        LOG_DEBUG("Connection failed result:security block");
         break;
     }
-    completed_.set_value(kInvalidConnectionInterfaceDescriptor);
-    connection_interface_manager_->ConnectionFailed(address_, psm_);
+    pending_fail_(result);
   }
 
- private:
-  ConnectionInterfaceManager* connection_interface_manager_;
+  std::string ToString() const {
+    return address_.ToString() + "." + std::to_string(psm_);
+  }
+
+  const ConnectionInterfaceDescriptor cid_;
   const l2cap::Psm psm_;
   const hci::Address address_;
-  ConnectionOpenCallback on_open_;
-  std::promise<uint16_t> completed_;
-};
-
-class ServiceInterface {
- public:
-  ServiceInterface(ConnectionInterfaceManager* connection_interface_manager, l2cap::Psm psm,
-                   ConnectionOpenCallback on_open, std::promise<void> completed)
-      : connection_interface_manager_(connection_interface_manager), psm_(psm), on_open_(on_open),
-        completed_(std::move(completed)) {}
-
-  void OnRegistrationComplete(l2cap::classic::DynamicChannelManager::RegistrationResult result,
-                              std::unique_ptr<l2cap::classic::DynamicChannelService> service) {
-    ASSERT(service_ == nullptr);
-    ASSERT(psm_ == service->GetPsm());
-    LOG_DEBUG("Registration is complete for psm:%hd", psm_);
-    service_ = std::move(service);
-    completed_.set_value();
-  }
-
-  void OnConnectionOpen(std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
-    LOG_DEBUG("Remote initiated connection is open from device:%s for psm:%hd", channel->GetDevice().ToString().c_str(),
-              psm_);
-    hci::Address address = channel->GetDevice();
-    ConnectionInterfaceDescriptor cid = connection_interface_manager_->AddChannel(std::move(channel));
-    connection_interface_manager_->ConnectionOpened(on_open_, address, psm_, cid);
-  }
-
-  l2cap::SecurityPolicy GetSecurityPolicy() const {
-    return security_policy_;
-  }
-
-  void RegisterService(
-      std::function<void(l2cap::Psm, l2cap::SecurityPolicy security_policy,
-                         l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete,
-                         l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_connection_open)>
-          func) {
-    func(psm_, security_policy_, common::BindOnce(&ServiceInterface::OnRegistrationComplete, common::Unretained(this)),
-         common::Bind(&ServiceInterface::OnConnectionOpen, common::Unretained(this)));
-  }
+  const ConnectionCompleteCallback on_complete_;
 
  private:
-  ConnectionInterfaceManager* connection_interface_manager_;
-  const l2cap::Psm psm_;
-  ConnectionOpenCallback on_open_;
-  std::promise<void> completed_;
+  const PendingConnectionOpen pending_open_;
+  const PendingConnectionFail pending_fail_;
 
-  std::unique_ptr<l2cap::classic::DynamicChannelService> service_;
-
-  const l2cap::SecurityPolicy security_policy_;
+  DISALLOW_COPY_AND_ASSIGN(PendingConnection);
 };
 
 struct L2cap::impl {
-  void RegisterService(l2cap::Psm psm, ConnectionOpenCallback on_open, std::promise<void> completed);
-  void UnregisterService(l2cap::Psm psm);
+  void RegisterService(
+      l2cap::Psm psm,
+      l2cap::classic::DynamicChannelConfigurationOption option,
+      ConnectionCompleteCallback on_complete,
+      RegisterServicePromise register_promise);
+  void UnregisterService(l2cap::Psm psm, UnregisterServicePromise unregister_promise);
 
-  void CreateConnection(l2cap::Psm psm, hci::Address address, ConnectionOpenCallback on_open,
-                        std::promise<uint16_t> completed);
+  void CreateConnection(
+      l2cap::Psm psm,
+      hci::Address address,
+      ConnectionCompleteCallback on_complete,
+      CreateConnectionPromise create_promise);
   void CloseConnection(ConnectionInterfaceDescriptor cid);
 
   void SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready);
@@ -379,54 +424,167 @@
 
   std::unique_ptr<l2cap::classic::DynamicChannelManager> dynamic_channel_manager_;
 
-  std::unordered_map<l2cap::Psm, std::shared_ptr<ServiceInterface>> psm_to_service_interface_map_;
-  std::unordered_map<l2cap::Psm, std::shared_ptr<PendingConnection>> psm_to_pending_connection_map_;
+  std::unordered_map<l2cap::Psm, std::unique_ptr<l2cap::classic::DynamicChannelService>> psm_to_service_interface_map_;
+
+  PendingConnectionId pending_connection_id_{0};
+  std::unordered_map<PendingConnectionId, std::unique_ptr<PendingConnection>> pending_connection_map_;
+
+  void PendingConnectionOpen(
+      PendingConnectionId id,
+      std::unique_ptr<PendingConnection> connection,
+      std::unique_ptr<l2cap::classic::DynamicChannel> channel);
+  void PendingConnectionFail(
+      PendingConnectionId id,
+      std::unique_ptr<PendingConnection> connection,
+      l2cap::classic::DynamicChannelManager::ConnectionResult result);
+  l2cap::classic::SecurityPolicy GetSecurityPolicy(l2cap::Psm psm) const;
 };
 
+const ModuleFactory L2cap::Factory = ModuleFactory([]() { return new L2cap(); });
+
 L2cap::impl::impl(L2cap& module, l2cap::classic::L2capClassicModule* l2cap_module)
-    : module_(module), l2cap_module_(l2cap_module), handler_(module_.GetHandler()),
+    : module_(module),
+      l2cap_module_(l2cap_module),
+      handler_(module_.GetHandler()),
       connection_interface_manager_(handler_) {
   dynamic_channel_manager_ = l2cap_module_->GetDynamicChannelManager();
 }
 
-void L2cap::impl::RegisterService(l2cap::Psm psm, ConnectionOpenCallback on_open, std::promise<void> completed) {
-  ASSERT(psm_to_service_interface_map_.find(psm) == psm_to_service_interface_map_.end());
-
-  auto service_interface =
-      std::make_shared<ServiceInterface>(&connection_interface_manager_, psm, on_open, std::move(completed));
-  psm_to_service_interface_map_.emplace(psm, service_interface);
-
-  // TODO(cmanton): Use the configuration option from user
-  service_interface->RegisterService(
-      [this](l2cap::Psm psm, l2cap::SecurityPolicy security_policy,
-             l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete,
-             l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_connection_open) {
-        bool rc = dynamic_channel_manager_->RegisterService(psm, l2cap::classic::DynamicChannelConfigurationOption(),
-                                                            security_policy, std::move(on_registration_complete),
-                                                            on_connection_open, handler_);
-        ASSERT_LOG(rc == true, "Failed to register classic service");
-      });
+l2cap::classic::SecurityPolicy L2cap::impl::GetSecurityPolicy(l2cap::Psm psm) const {
+  if (psm == 1) {
+    return l2cap::classic::SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK;
+  } else {
+    return l2cap::classic::SecurityPolicy::ENCRYPTED_TRANSPORT;
+  }
 }
 
-void L2cap::impl::UnregisterService(l2cap::Psm psm) {
-  psm_to_service_interface_map_.erase(psm);
+void L2cap::impl::RegisterService(
+    l2cap::Psm psm,
+    l2cap::classic::DynamicChannelConfigurationOption option,
+    ConnectionCompleteCallback on_complete,
+    RegisterServicePromise register_promise) {
+  const l2cap::classic::SecurityPolicy security_policy = GetSecurityPolicy(psm);
+
+  dynamic_channel_manager_->RegisterService(
+      psm,
+      option,
+      security_policy,
+      handler_->BindOnce(
+          [](RegisterServicePromise register_promise,
+             std::unordered_map<l2cap::Psm, std::unique_ptr<l2cap::classic::DynamicChannelService>>*
+                 psm_to_service_interface_map_,
+             l2cap::classic::DynamicChannelManager::RegistrationResult result,
+             std::unique_ptr<l2cap::classic::DynamicChannelService> service) {
+            std::unique_ptr<l2cap::classic::DynamicChannelService> service_ = std::move(service);
+            switch (result) {
+              case l2cap::classic::DynamicChannelManager::RegistrationResult::SUCCESS:
+                LOG_DEBUG("Service is registered for psm:%hd", service_->GetPsm());
+                register_promise.set_value(service_->GetPsm());
+                psm_to_service_interface_map_->emplace(service_->GetPsm(), std::move(service_));
+                break;
+              case l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE:
+                LOG_WARN("Failed to register duplicate service has psm:%hd", service_->GetPsm());
+                register_promise.set_value(l2cap::kDefaultPsm);
+                break;
+              case l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE:
+                LOG_WARN("Failed to register invalid service psm:%hd", service_->GetPsm());
+                register_promise.set_value(l2cap::kDefaultPsm);
+                break;
+            }
+          },
+          std::move(register_promise),
+          &psm_to_service_interface_map_),
+      handler_->Bind(
+          [](l2cap::Psm psm,
+             ConnectionCompleteCallback on_complete,
+             ConnectionInterfaceManager* connection_interface_manager_,
+             std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+            LOG_DEBUG("Remote initiated connection is open from device:%s", channel->GetDevice().ToString().c_str());
+
+            ConnectionInterfaceDescriptor cid = connection_interface_manager_->AllocateConnectionInterfaceDescriptor();
+            uint16_t remote_cid = channel->HACK_GetRemoteCid();
+            connection_interface_manager_->AddConnection(cid, std::move(channel));
+            connection_interface_manager_->ConnectionOpened(on_complete, psm, cid, remote_cid);
+            LOG_DEBUG("connection open");
+          },
+          psm,
+          on_complete,
+          &connection_interface_manager_));
 }
 
-void L2cap::impl::CreateConnection(l2cap::Psm psm, hci::Address address, ConnectionOpenCallback on_open,
-                                   std::promise<uint16_t> completed) {
-  LOG_DEBUG("Initiating classic connection to psm:%hd device:%s", psm, address.ToString().c_str());
-  auto pending_connection = std::make_shared<PendingConnection>(&connection_interface_manager_, psm, address,
-                                                                std::move(on_open), std::move(completed));
-  // TODO(cmanton) hash psm/address pair into unordered map for pending_connection
-  // This is ok for now
-  psm_to_pending_connection_map_[psm] = pending_connection;
-  // TODO(cmanton): Add ERTM mode support by changing configuratio_option in ConnectChannel()
-  bool rc = dynamic_channel_manager_->ConnectChannel(
-      address, l2cap::classic::DynamicChannelConfigurationOption(), psm,
-      common::Bind(&PendingConnection::OnConnectionOpen, common::Unretained(pending_connection.get())),
-      common::BindOnce(&PendingConnection::OnConnectionFailure, common::Unretained(pending_connection.get())),
-      handler_);
-  ASSERT_LOG(rc == true, "Failed to create classic connection");
+void L2cap::impl::UnregisterService(l2cap::Psm psm, UnregisterServicePromise unregister_promise) {
+  ASSERT(psm_to_service_interface_map_.find(psm) != psm_to_service_interface_map_.end());
+
+  psm_to_service_interface_map_[psm]->Unregister(handler_->BindOnce(
+      [](std::unordered_map<l2cap::Psm, std::unique_ptr<l2cap::classic::DynamicChannelService>>*
+             psm_to_service_interface_map_,
+         UnregisterServicePromise unregister_promise,
+         l2cap::Psm psm) {
+        psm_to_service_interface_map_->erase(psm);
+        unregister_promise.set_value();
+      },
+      &psm_to_service_interface_map_,
+      std::move(unregister_promise),
+      psm));
+}
+
+void L2cap::impl::PendingConnectionOpen(
+    PendingConnectionId id,
+    std::unique_ptr<PendingConnection> connection,
+    std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+  uint16_t remote_cid = channel->HACK_GetRemoteCid();
+  connection_interface_manager_.AddConnection(connection->cid_, std::move(channel));
+  connection_interface_manager_.ConnectionOpened(
+      std::move(connection->on_complete_), connection->psm_, connection->cid_, remote_cid);
+  pending_connection_map_.erase(id);
+}
+
+void L2cap::impl::PendingConnectionFail(
+    PendingConnectionId id,
+    std::unique_ptr<PendingConnection> connection,
+    l2cap::classic::DynamicChannelManager::ConnectionResult result) {
+  connection_interface_manager_.ConnectionFailed(
+      std::move(connection->on_complete_), connection->address_, connection->psm_, connection->cid_);
+  connection_interface_manager_.FreeConnectionInterfaceDescriptor(connection->cid_);
+  pending_connection_map_.erase(id);
+}
+
+void L2cap::impl::CreateConnection(
+    l2cap::Psm psm,
+    hci::Address address,
+    ConnectionCompleteCallback on_complete,
+    CreateConnectionPromise create_promise) {
+  ConnectionInterfaceDescriptor cid = connection_interface_manager_.AllocateConnectionInterfaceDescriptor();
+  create_promise.set_value(cid);
+
+  if (cid == kInvalidConnectionInterfaceDescriptor) {
+    LOG_WARN("No resources to create a connection");
+    return;
+  }
+
+  PendingConnectionId id = ++pending_connection_id_;
+  pending_connection_map_.emplace(
+      id,
+      std::make_unique<PendingConnection>(
+          cid,
+          psm,
+          address,
+          on_complete,
+          [this, id](std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+            auto connection = std::move(pending_connection_map_.at(id));
+            handler_->CallOn(this, &L2cap::impl::PendingConnectionOpen, id, std::move(connection), std::move(channel));
+          },
+          [this, id](l2cap::classic::DynamicChannelManager::ConnectionResult result) {
+            auto connection = std::move(pending_connection_map_.at(id));
+            handler_->CallOn(this, &L2cap::impl::PendingConnectionFail, id, std::move(connection), result);
+          }));
+
+  dynamic_channel_manager_->ConnectChannel(
+      address,
+      l2cap::classic::DynamicChannelConfigurationOption(),
+      psm,
+      handler_->BindOn(pending_connection_map_.at(id).get(), &PendingConnection::OnConnectionOpen),
+      handler_->BindOnceOn(pending_connection_map_.at(id).get(), &PendingConnection::OnConnectionFailure));
 }
 
 void L2cap::impl::CloseConnection(ConnectionInterfaceDescriptor cid) {
@@ -449,25 +607,50 @@
   function();
 }
 
-void L2cap::RegisterService(uint16_t raw_psm, ConnectionOpenCallback on_open, std::promise<void> completed) {
+void L2cap::RegisterService(
+    uint16_t raw_psm,
+    bool use_ertm,
+    uint16_t mtu,
+    ConnectionCompleteCallback on_complete,
+    RegisterServicePromise register_promise) {
   l2cap::Psm psm{raw_psm};
-  GetHandler()->Post(common::BindOnce(&L2cap::impl::RegisterService, common::Unretained(pimpl_.get()), psm, on_open,
-                                      std::move(completed)));
+  l2cap::classic::DynamicChannelConfigurationOption option;
+  if (use_ertm) {
+    option.channel_mode =
+        l2cap::classic::DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION;
+  }
+  option.incoming_mtu = mtu;
+  GetHandler()->Post(common::BindOnce(
+      &L2cap::impl::RegisterService,
+      common::Unretained(pimpl_.get()),
+      psm,
+      option,
+      on_complete,
+      std::move(register_promise)));
 }
 
-void L2cap::UnregisterService(uint16_t raw_psm) {
+void L2cap::UnregisterService(uint16_t raw_psm, UnregisterServicePromise unregister_promise) {
   l2cap::Psm psm{raw_psm};
-  GetHandler()->Post(common::Bind(&L2cap::impl::UnregisterService, common::Unretained(pimpl_.get()), psm));
+  GetHandler()->Post(common::BindOnce(
+      &L2cap::impl::UnregisterService, common::Unretained(pimpl_.get()), psm, std::move(unregister_promise)));
 }
 
-void L2cap::CreateConnection(uint16_t raw_psm, const std::string address_string, ConnectionOpenCallback on_open,
-                             std::promise<uint16_t> completed) {
+void L2cap::CreateConnection(
+    uint16_t raw_psm,
+    const std::string address_string,
+    ConnectionCompleteCallback on_complete,
+    CreateConnectionPromise create_promise) {
   l2cap::Psm psm{raw_psm};
   hci::Address address;
   hci::Address::FromString(address_string, address);
 
-  GetHandler()->Post(common::BindOnce(&L2cap::impl::CreateConnection, common::Unretained(pimpl_.get()), psm, address,
-                                      on_open, std::move(completed)));
+  GetHandler()->Post(common::BindOnce(
+      &L2cap::impl::CreateConnection,
+      common::Unretained(pimpl_.get()),
+      psm,
+      address,
+      on_complete,
+      std::move(create_promise)));
 }
 
 void L2cap::CloseConnection(uint16_t raw_cid) {
@@ -483,8 +666,8 @@
 
 void L2cap::SetConnectionClosedCallback(uint16_t raw_cid, ConnectionClosedCallback on_closed) {
   ConnectionInterfaceDescriptor cid(raw_cid);
-  GetHandler()->Post(common::Bind(&L2cap::impl::SetConnectionClosedCallback, common::Unretained(pimpl_.get()), cid,
-                                  std::move(on_closed)));
+  GetHandler()->Post(common::Bind(
+      &L2cap::impl::SetConnectionClosedCallback, common::Unretained(pimpl_.get()), cid, std::move(on_closed)));
 }
 
 void L2cap::Write(uint16_t raw_cid, const uint8_t* data, size_t len) {
@@ -493,16 +676,6 @@
   GetHandler()->Post(common::BindOnce(&L2cap::impl::Write, common::Unretained(pimpl_.get()), cid, std::move(packet)));
 }
 
-void L2cap::WriteFlushable(uint16_t raw_cid, const uint8_t* data, size_t len) {
-  LOG_WARN("UNIMPLEMENTED Write flushable");
-  return Write(raw_cid, data, len);
-}
-
-void L2cap::WriteNonFlushable(uint16_t raw_cid, const uint8_t* data, size_t len) {
-  LOG_WARN("UNIMPLEMENTED Write non flushable");
-  return Write(raw_cid, data, len);
-}
-
 void L2cap::SendLoopbackResponse(std::function<void()> function) {
   GetHandler()->Post(common::BindOnce(&L2cap::impl::SendLoopbackResponse, common::Unretained(pimpl_.get()), function));
 }
@@ -522,5 +695,9 @@
   pimpl_.reset();
 }
 
+std::string L2cap::ToString() const {
+  return kModuleName;
+}
+
 }  // namespace shim
 }  // namespace bluetooth
diff --git a/gd/shim/l2cap.h b/gd/shim/l2cap.h
index 955d20c..5c971ad 100644
--- a/gd/shim/l2cap.h
+++ b/gd/shim/l2cap.h
@@ -22,28 +22,42 @@
 #include <string>
 
 #include "module.h"
-#include "shim/il2cap.h"
 
 namespace bluetooth {
 namespace shim {
 
-class L2cap : public bluetooth::Module, public bluetooth::shim::IL2cap {
+using ConnectionClosedCallback = std::function<void(uint16_t cid, int error_code)>;
+using ConnectionCompleteCallback =
+    std::function<void(std::string string_address, uint16_t psm, uint16_t cid, uint16_t remote_cid, bool is_connected)>;
+using ReadDataReadyCallback = std::function<void(uint16_t cid, std::vector<const uint8_t> data)>;
+
+using RegisterServicePromise = std::promise<uint16_t>;
+using UnregisterServicePromise = std::promise<void>;
+using CreateConnectionPromise = std::promise<uint16_t>;
+
+class L2cap : public bluetooth::Module {
  public:
-  void RegisterService(uint16_t psm, ConnectionOpenCallback on_open, std::promise<void> completed) override;
-  void UnregisterService(uint16_t psm) override;
+  void RegisterService(
+      uint16_t psm,
+      bool use_ertm,
+      uint16_t mtu,
+      ConnectionCompleteCallback on_complete,
+      RegisterServicePromise register_promise);
+  void UnregisterService(uint16_t psm, UnregisterServicePromise unregister_promise);
 
-  void CreateConnection(uint16_t psm, const std::string address_string, ConnectionOpenCallback on_open,
-                        std::promise<uint16_t> completed) override;
-  void CloseConnection(uint16_t cid) override;
+  void CreateConnection(
+      uint16_t psm,
+      const std::string address_string,
+      ConnectionCompleteCallback on_complete,
+      CreateConnectionPromise create_promise);
+  void CloseConnection(uint16_t cid);
 
-  void SetReadDataReadyCallback(uint16_t cid, ReadDataReadyCallback on_data_ready) override;
-  void SetConnectionClosedCallback(uint16_t cid, ConnectionClosedCallback on_closed) override;
+  void SetReadDataReadyCallback(uint16_t cid, ReadDataReadyCallback on_data_ready);
+  void SetConnectionClosedCallback(uint16_t cid, ConnectionClosedCallback on_closed);
 
-  void Write(uint16_t cid, const uint8_t* data, size_t len) override;
-  void WriteFlushable(uint16_t cid, const uint8_t* data, size_t len) override;
-  void WriteNonFlushable(uint16_t cid, const uint8_t* data, size_t len) override;
+  void Write(uint16_t cid, const uint8_t* data, size_t len);
 
-  void SendLoopbackResponse(std::function<void()>) override;
+  void SendLoopbackResponse(std::function<void()>);
 
   L2cap() = default;
   ~L2cap() = default;
@@ -54,6 +68,7 @@
   void ListDependencies(ModuleList* list) override;  // Module
   void Start() override;                             // Module
   void Stop() override;                              // Module
+  std::string ToString() const override;             // Module
 
  private:
   struct impl;
diff --git a/gd/shim/l2cap_test.cc b/gd/shim/l2cap_test.cc
new file mode 100644
index 0000000..ed9fe63
--- /dev/null
+++ b/gd/shim/l2cap_test.cc
@@ -0,0 +1,491 @@
+/*
+ * 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.
+ */
+
+#include "shim/l2cap.h"
+
+#include <gtest/gtest.h>
+
+#include <future>
+#include <memory>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/address_with_type.h"
+#include "l2cap/classic/dynamic_channel_configuration_option.h"
+#include "l2cap/classic/dynamic_channel_manager.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h"
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/classic/security_policy.h"
+#include "l2cap/internal/ilink.h"
+#include "l2cap/psm.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace shim {
+namespace {
+
+constexpr uint16_t kPsm = 123;
+constexpr uint16_t kPsm2 = kPsm + 2;
+constexpr uint16_t kCid = 456;
+constexpr uint16_t kCid2 = kCid + 1;
+constexpr char device_address[] = "11:22:33:44:55:66";
+constexpr char device_address2[] = "aa:bb:cc:dd:ee:ff";
+constexpr bool kNoUseErtm = false;
+constexpr uint16_t kMtu = 1000;
+
+class TestDynamicChannelService : public l2cap::classic::DynamicChannelService {
+ public:
+  TestDynamicChannelService(
+      l2cap::Psm psm, l2cap::classic::internal::DynamicChannelServiceManagerImpl* manager, os::Handler* handler)
+      : DynamicChannelService(psm, manager, handler) {}
+};
+
+class TestLink : public l2cap::internal::ILink {
+ public:
+  hci::AddressWithType GetDevice() {
+    return device_with_type_;
+  }
+  hci::AddressWithType device_with_type_;
+
+  void SendLeCredit(l2cap::Cid local_cid, uint16_t credit) {}
+
+  void SendDisconnectionRequest(l2cap::Cid cid, l2cap::Cid remote_cid) {
+    connection_closed_promise_.set_value();
+  }
+  std::promise<void> connection_closed_promise_;
+};
+
+class TestDynamicChannelManagerImpl {
+ public:
+  void ConnectChannel(
+      hci::Address device,
+      l2cap::classic::DynamicChannelConfigurationOption configuration_option,
+      l2cap::Psm psm,
+      l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback,
+      l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback) {
+    connections_++;
+    on_open_callback_ = std::move(on_open_callback);
+    on_fail_callback_ = std::move(on_fail_callback);
+
+    connected_promise_.set_value();
+  }
+  int connections_{0};
+
+  void RegisterService(
+      l2cap::Psm psm,
+      l2cap::classic::DynamicChannelConfigurationOption configuration_option,
+      const l2cap::classic::SecurityPolicy& security_policy,
+      l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete,
+      l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback) {
+    services_++;
+    on_registration_complete_ = std::move(on_registration_complete);
+    on_open_callback_ = std::move(on_open_callback);
+
+    register_promise_.set_value();
+  }
+  int services_{0};
+
+  void SetConnectionFuture() {
+    connected_promise_ = std::promise<void>();
+  }
+
+  void WaitConnectionFuture() {
+    connected_future_ = connected_promise_.get_future();
+    connected_future_.wait();
+  }
+
+  void SetRegistrationFuture() {
+    register_promise_ = std::promise<void>();
+  }
+
+  void WaitRegistrationFuture() {
+    register_future_ = register_promise_.get_future();
+    register_future_.wait();
+  }
+
+  void SetConnectionOnFail(l2cap::classic::DynamicChannelManager::ConnectionResult result, std::promise<void> promise) {
+    std::move(on_fail_callback_).Invoke(result);
+    promise.set_value();
+  }
+
+  void SetConnectionOnOpen(std::unique_ptr<l2cap::DynamicChannel> channel, std::promise<void> promise) {
+    std::move(on_open_callback_).Invoke(std::move(channel));
+    promise.set_value();
+  }
+
+  l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete_{};
+  l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback_{};
+  l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback_{};
+
+  TestDynamicChannelManagerImpl() = default;
+  ~TestDynamicChannelManagerImpl() = default;
+
+ private:
+  std::promise<void> connected_promise_;
+  std::future<void> connected_future_;
+
+  std::promise<void> register_promise_;
+  std::future<void> register_future_;
+};
+
+class TestDynamicChannelManager : public l2cap::classic::DynamicChannelManager {
+ public:
+  void ConnectChannel(
+      hci::Address device,
+      l2cap::classic::DynamicChannelConfigurationOption configuration_option,
+      l2cap::Psm psm,
+      l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback,
+      l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback) override {
+    impl_.ConnectChannel(device, configuration_option, psm, std::move(on_open_callback), std::move(on_fail_callback));
+  }
+
+  void RegisterService(
+      l2cap::Psm psm,
+      l2cap::classic::DynamicChannelConfigurationOption configuration_option,
+      const l2cap::classic::SecurityPolicy& security_policy,
+      l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete,
+      l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback) override {
+    impl_.RegisterService(
+        psm, configuration_option, security_policy, std::move(on_registration_complete), std::move(on_open_callback));
+  }
+  TestDynamicChannelManager(TestDynamicChannelManagerImpl& impl) : impl_(impl) {}
+  TestDynamicChannelManagerImpl& impl_;
+};
+
+class TestL2capClassicModule : public l2cap::classic::L2capClassicModule {
+ public:
+  std::unique_ptr<l2cap::classic::DynamicChannelManager> GetDynamicChannelManager() override {
+    return std::make_unique<TestDynamicChannelManager>(*impl_);
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override;
+  void Stop() override;
+
+  std::unique_ptr<TestDynamicChannelManagerImpl> impl_;
+};
+
+void TestL2capClassicModule::Start() {
+  impl_ = std::make_unique<TestDynamicChannelManagerImpl>();
+  ASSERT_NE(impl_, nullptr);
+}
+
+void TestL2capClassicModule::Stop() {
+  impl_.reset();
+}
+
+class ShimL2capTest : public ::testing::Test {
+ public:
+  void OnConnectionComplete(std::string string_address, uint16_t psm, uint16_t cid, bool connected) {
+    connection_string_address_ = string_address;
+    connection_psm_ = psm;
+    connection_cid_ = cid;
+    connection_connected_ = connected;
+    connection_complete_promise_.set_value();
+  }
+
+  uint16_t CreateConnection(uint16_t psm, std::string device_address) {
+    std::promise<uint16_t> promise;
+    auto future = promise.get_future();
+
+    shim_l2cap_->CreateConnection(
+        psm,
+        device_address,
+        std::bind(
+            &bluetooth::shim::ShimL2capTest::OnConnectionComplete,
+            this,
+            std::placeholders::_1,
+            std::placeholders::_2,
+            std::placeholders::_3,
+            std::placeholders::_4),
+        std::move(promise));
+    return future.get();
+  }
+
+  void SetConnectionFuture() {
+    test_l2cap_classic_module_->impl_->SetConnectionFuture();
+  }
+
+  void WaitConnectionFuture() {
+    test_l2cap_classic_module_->impl_->WaitConnectionFuture();
+  }
+
+  void SetRegistrationFuture() {
+    test_l2cap_classic_module_->impl_->SetRegistrationFuture();
+  }
+
+  void WaitRegistrationFuture() {
+    test_l2cap_classic_module_->impl_->WaitRegistrationFuture();
+  }
+
+  int NumberOfConnections() const {
+    return test_l2cap_classic_module_->impl_->connections_;
+  }
+
+  int NumberOfServices() const {
+    return test_l2cap_classic_module_->impl_->services_;
+  }
+
+  std::string connection_string_address_;
+  uint16_t connection_psm_{0};
+  uint16_t connection_cid_{0};
+  bool connection_connected_{false};
+
+  shim::L2cap* shim_l2cap_ = nullptr;
+  TestL2capClassicModule* test_l2cap_classic_module_{nullptr};
+
+  TestLink test_link_;
+  std::promise<void> connection_complete_promise_;
+
+ protected:
+  void SetUp() override {
+    handler_ = new os::Handler(&thread_);
+
+    test_l2cap_classic_module_ = new TestL2capClassicModule();
+    test_l2cap_classic_module_->Start();
+    fake_registry_.InjectTestModule(&l2cap::classic::L2capClassicModule::Factory, test_l2cap_classic_module_);
+
+    fake_registry_.Start<shim::L2cap>(&thread_);
+    shim_l2cap_ = static_cast<shim::L2cap*>(fake_registry_.GetModuleUnderTest(&shim::L2cap::Factory));
+  }
+
+  void TearDown() override {
+    fake_registry_.StopAll();
+    handler_->Clear();
+    delete handler_;
+  }
+  os::Handler* handler_ = nullptr;
+  l2cap::classic::internal::testing::MockDynamicChannelServiceManagerImpl mock_;
+
+ private:
+  TestModuleRegistry fake_registry_;
+  os::Thread& thread_ = fake_registry_.GetTestThread();
+};
+
+TEST_F(ShimL2capTest, CreateThenDisconnectBeforeCompletion) {
+  SetConnectionFuture();
+
+  ASSERT_EQ(NumberOfConnections(), 0);
+  uint16_t cid = CreateConnection(kPsm, device_address);
+  ASSERT_NE(cid, 0);
+
+  WaitConnectionFuture();
+  ASSERT_EQ(NumberOfConnections(), 1);
+
+  shim_l2cap_->CloseConnection(cid);
+}
+
+TEST_F(ShimL2capTest, MaxCreatedConnections) {
+  for (int i = 0; i < 65536 - 64; i++) {
+    SetConnectionFuture();
+    uint16_t cid = CreateConnection(kPsm, device_address);
+    ASSERT_NE(cid, 0);
+    WaitConnectionFuture();
+
+    ASSERT_EQ(NumberOfConnections(), i + 1);
+  }
+  uint16_t cid = CreateConnection(kPsm, device_address);
+  ASSERT_EQ(cid, 0);
+
+  ASSERT_EQ(NumberOfConnections(), 65536 - 64);
+}
+
+TEST_F(ShimL2capTest, TwoDifferentCreatedConnections) {
+  {
+    SetConnectionFuture();
+    uint16_t cid = CreateConnection(kPsm, device_address);
+    ASSERT_NE(cid, 0);
+    WaitConnectionFuture();
+
+    ASSERT_EQ(NumberOfConnections(), 1);
+  }
+
+  {
+    SetConnectionFuture();
+    uint16_t cid = CreateConnection(kPsm2, device_address2);
+    ASSERT_NE(cid, 0);
+    WaitConnectionFuture();
+
+    ASSERT_EQ(NumberOfConnections(), 2);
+  }
+}
+
+TEST_F(ShimL2capTest, ConnectFail) {
+  SetConnectionFuture();
+  uint16_t cid = CreateConnection(kPsm, device_address);
+  ASSERT_NE(cid, 0);
+  WaitConnectionFuture();
+
+  ASSERT_EQ(NumberOfConnections(), 1);
+
+  l2cap::classic::DynamicChannelManager::ConnectionResult result{
+      .connection_result_code = TestDynamicChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED,
+      .hci_error = hci::ErrorCode::SUCCESS,
+      .l2cap_connection_response_result = l2cap::ConnectionResponseResult::SUCCESS,
+  };
+
+  std::promise<void> on_fail_promise;
+  auto on_fail_future = on_fail_promise.get_future();
+  handler_->CallOn(
+      test_l2cap_classic_module_->impl_.get(),
+      &TestDynamicChannelManagerImpl::SetConnectionOnFail,
+      result,
+      std::move(on_fail_promise));
+  on_fail_future.wait();
+
+  ASSERT_EQ(connection_connected_, false);
+
+  shim_l2cap_->CloseConnection(cid);
+}
+
+TEST_F(ShimL2capTest, ConnectOpen) {
+  SetConnectionFuture();
+  uint16_t cid = CreateConnection(kPsm, device_address);
+  ASSERT_NE(cid, 0);
+  WaitConnectionFuture();
+
+  ASSERT_EQ(NumberOfConnections(), 1);
+
+  hci::Address address;
+  hci::Address::FromString(device_address, address);
+  test_link_.device_with_type_ = hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS);
+
+  l2cap::Psm psm = kPsm;
+  l2cap::Cid local_cid = kCid;
+  l2cap::Cid remote_cid = kCid2;
+
+  std::shared_ptr<l2cap::internal::DynamicChannelImpl> impl =
+      std::make_shared<l2cap::internal::DynamicChannelImpl>(psm, local_cid, remote_cid, &test_link_, handler_);
+
+  auto channel = std::make_unique<l2cap::DynamicChannel>(impl, handler_);
+
+  std::promise<void> on_fail_promise;
+  auto on_fail_future = on_fail_promise.get_future();
+
+  auto connection_complete_future = connection_complete_promise_.get_future();
+  handler_->CallOn(
+      test_l2cap_classic_module_->impl_.get(),
+      &TestDynamicChannelManagerImpl::SetConnectionOnOpen,
+      std::move(channel),
+      std::move(on_fail_promise));
+  connection_complete_future.wait();
+
+  on_fail_future.wait();
+
+  ASSERT_EQ(connection_connected_, true);
+
+  auto future = test_link_.connection_closed_promise_.get_future();
+  shim_l2cap_->CloseConnection(cid);
+  future.wait();
+}
+
+TEST_F(ShimL2capTest, RegisterService_Success) {
+  std::promise<uint16_t> registration_promise;
+  auto registration_pending = registration_promise.get_future();
+
+  SetRegistrationFuture();
+  shim_l2cap_->RegisterService(
+      kPsm,
+      kNoUseErtm,
+      kMtu,
+      std::bind(
+          &bluetooth::shim::ShimL2capTest::OnConnectionComplete,
+          this,
+          std::placeholders::_1,
+          std::placeholders::_2,
+          std::placeholders::_3,
+          std::placeholders::_4),
+      std::move(registration_promise));
+  WaitRegistrationFuture();
+  ASSERT_LOG(!test_l2cap_classic_module_->impl_->on_registration_complete_.IsEmpty(), "Synchronization failure");
+  ASSERT_EQ(test_l2cap_classic_module_->impl_->services_, 1);
+
+  l2cap::classic::DynamicChannelManager::RegistrationResult result{
+      l2cap::classic::DynamicChannelManager::RegistrationResult::SUCCESS,
+  };
+  auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_);
+
+  test_l2cap_classic_module_->impl_->on_registration_complete_.Invoke(result, std::move(service));
+  uint16_t psm = registration_pending.get();
+  ASSERT_EQ(psm, kPsm);
+}
+
+TEST_F(ShimL2capTest, RegisterService_Duplicate) {
+  std::promise<uint16_t> promise;
+  auto future = promise.get_future();
+
+  SetRegistrationFuture();
+  shim_l2cap_->RegisterService(
+      kPsm,
+      kNoUseErtm,
+      kMtu,
+      std::bind(
+          &bluetooth::shim::ShimL2capTest::OnConnectionComplete,
+          this,
+          std::placeholders::_1,
+          std::placeholders::_2,
+          std::placeholders::_3,
+          std::placeholders::_4),
+      std::move(promise));
+  WaitRegistrationFuture();
+  ASSERT_LOG(!test_l2cap_classic_module_->impl_->on_registration_complete_.IsEmpty(), "Synchronization failure");
+  ASSERT_EQ(test_l2cap_classic_module_->impl_->services_, 1);
+
+  l2cap::classic::DynamicChannelManager::RegistrationResult result{
+      l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE,
+  };
+  auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_);
+
+  test_l2cap_classic_module_->impl_->on_registration_complete_.Invoke(result, std::move(service));
+  uint16_t psm = future.get();
+  ASSERT_EQ(psm, l2cap::kDefaultPsm);
+}
+
+TEST_F(ShimL2capTest, RegisterService_Invalid) {
+  std::promise<uint16_t> promise;
+  auto future = promise.get_future();
+
+  SetRegistrationFuture();
+
+  shim_l2cap_->RegisterService(
+      kPsm,
+      kNoUseErtm,
+      kMtu,
+      std::bind(
+          &bluetooth::shim::ShimL2capTest::OnConnectionComplete,
+          this,
+          std::placeholders::_1,
+          std::placeholders::_2,
+          std::placeholders::_3,
+          std::placeholders::_4),
+      std::move(promise));
+
+  l2cap::classic::DynamicChannelManager::RegistrationResult result{
+      l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE,
+  };
+  auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_);
+  WaitRegistrationFuture();
+
+  ASSERT_LOG(!test_l2cap_classic_module_->impl_->on_registration_complete_.IsEmpty(), "Synchronization failure");
+  test_l2cap_classic_module_->impl_->on_registration_complete_.Invoke(result, std::move(service));
+  uint16_t psm = future.get();
+  ASSERT_EQ(psm, l2cap::kDefaultPsm);
+  ASSERT_EQ(test_l2cap_classic_module_->impl_->services_, 1);
+}
+
+}  // namespace
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/shim/name.cc b/gd/shim/name.cc
deleted file mode 100644
index 582d1ed..0000000
--- a/gd/shim/name.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "bt_gd_shim"
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <unordered_map>
-
-#include "hci/address.h"
-#include "hci/hci_packets.h"
-#include "module.h"
-#include "neighbor/name.h"
-#include "os/handler.h"
-#include "os/log.h"
-#include "shim/name.h"
-
-namespace bluetooth {
-namespace shim {
-
-struct Name::impl {
-  void ReadRemoteNameRequest(const hci::Address address, hci::PageScanRepetitionMode page_scan_repetition_mode,
-                             uint16_t clock_offset, hci::ClockOffsetValid clock_offset_valid,
-                             ReadRemoteNameCallback callback);
-  void CancelRemoteNameRequest(const hci::Address address, CancelRemoteNameCallback callback);
-
-  void OnReadRemoteName(hci::ErrorCode status, hci::Address address, std::array<uint8_t, 248> name);
-  void OnCancelRemoteName(hci::ErrorCode status, hci::Address address);
-
-  impl(neighbor::NameModule* module, os::Handler* handler);
-  ~impl();
-
- private:
-  std::unordered_map<hci::Address, ReadRemoteNameCallback> address_to_read_remote_callback_map_;
-  std::unordered_map<hci::Address, CancelRemoteNameCallback> address_to_cancel_remote_callback_map_;
-
-  neighbor::NameModule* module_{nullptr};
-  os::Handler* handler_;
-};
-
-const ModuleFactory Name::Factory = ModuleFactory([]() { return new Name(); });
-
-void Name::impl::OnReadRemoteName(hci::ErrorCode status, hci::Address address, std::array<uint8_t, 248> name) {
-  LOG_DEBUG("%s from %s", __func__, address.ToString().c_str());
-  ASSERT(address_to_read_remote_callback_map_.find(address) != address_to_read_remote_callback_map_.end());
-  ReadRemoteNameCallback callback = address_to_read_remote_callback_map_[address];
-  address_to_read_remote_callback_map_.erase(address);
-  callback(address.ToString(), static_cast<uint8_t>(status), name);
-}
-
-void Name::impl::OnCancelRemoteName(hci::ErrorCode status, hci::Address address) {
-  LOG_DEBUG("%s from %s", __func__, address.ToString().c_str());
-  ASSERT(address_to_cancel_remote_callback_map_.find(address) != address_to_cancel_remote_callback_map_.end());
-  CancelRemoteNameCallback callback = address_to_cancel_remote_callback_map_[address];
-  address_to_cancel_remote_callback_map_.erase(address);
-  callback(address.ToString(), static_cast<uint8_t>(status));
-}
-
-void Name::impl::ReadRemoteNameRequest(const hci::Address address,
-                                       hci::PageScanRepetitionMode page_scan_repetition_mode, uint16_t clock_offset,
-                                       hci::ClockOffsetValid clock_offset_valid, ReadRemoteNameCallback callback) {
-  ASSERT(address_to_read_remote_callback_map_.find(address) == address_to_read_remote_callback_map_.end());
-  address_to_read_remote_callback_map_[address] = callback;
-  module_->ReadRemoteNameRequest(address, page_scan_repetition_mode, clock_offset, clock_offset_valid,
-                                 common::BindOnce(&Name::impl::OnReadRemoteName, common::Unretained(this)), handler_);
-}
-
-void Name::impl::CancelRemoteNameRequest(const hci::Address address, CancelRemoteNameCallback callback) {
-  ASSERT(address_to_cancel_remote_callback_map_.find(address) == address_to_cancel_remote_callback_map_.end());
-  address_to_cancel_remote_callback_map_[address] = callback;
-  module_->CancelRemoteNameRequest(address, common::BindOnce(&Name::impl::OnCancelRemoteName, common::Unretained(this)),
-                                   handler_);
-}
-
-Name::impl::impl(neighbor::NameModule* module, os::Handler* handler) : module_(module), handler_(handler) {}
-
-Name::impl::~impl() {}
-
-void Name::ReadRemoteNameRequest(std::string remote_address, ReadRemoteNameCallback callback) {
-  hci::Address address;
-  hci::Address::FromString(remote_address, address);
-
-  // TODO(cmanton) Use remote name request defaults for now
-  hci::PageScanRepetitionMode page_scan_repetition_mode = hci::PageScanRepetitionMode::R1;
-  uint16_t clock_offset = 0;
-  hci::ClockOffsetValid clock_offset_valid = hci::ClockOffsetValid::INVALID;
-  pimpl_->ReadRemoteNameRequest(address, page_scan_repetition_mode, clock_offset, clock_offset_valid, callback);
-}
-
-void Name::CancelRemoteNameRequest(std::string remote_address, CancelRemoteNameCallback callback) {
-  hci::Address address;
-  hci::Address::FromString(remote_address, address);
-  pimpl_->CancelRemoteNameRequest(address, callback);
-}
-
-/**
- * Module methods
- */
-void Name::ListDependencies(ModuleList* list) {
-  list->add<neighbor::NameModule>();
-}
-
-void Name::Start() {
-  pimpl_ = std::make_unique<impl>(GetDependency<neighbor::NameModule>(), GetHandler());
-}
-
-void Name::Stop() {
-  pimpl_.reset();
-}
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/name.h b/gd/shim/name.h
deleted file mode 100644
index 7d8233f..0000000
--- a/gd/shim/name.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <memory>
-
-#include "module.h"
-#include "shim/iname.h"
-
-namespace bluetooth {
-namespace shim {
-
-class Name : public bluetooth::Module, public bluetooth::shim::IName {
- public:
-  void ReadRemoteNameRequest(std::string remote_address, ReadRemoteNameCallback callback) override;
-  void CancelRemoteNameRequest(std::string remote_address, CancelRemoteNameCallback callback) override;
-
-  Name() = default;
-  ~Name() = default;
-
-  static const ModuleFactory Factory;
-
- protected:
-  void ListDependencies(ModuleList* list) override;  // Module
-  void Start() override;                             // Module
-  void Stop() override;                              // Module
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-  DISALLOW_COPY_AND_ASSIGN(Name);
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/only_include_this_file_into_legacy_stack___ever.h b/gd/shim/only_include_this_file_into_legacy_stack___ever.h
index 335778a..2d4baea 100644
--- a/gd/shim/only_include_this_file_into_legacy_stack___ever.h
+++ b/gd/shim/only_include_this_file_into_legacy_stack___ever.h
@@ -24,14 +24,9 @@
  * Only common data structures should be used to pass data between the stacks.
  *
  */
-#include "gd/shim/iadvertising.h"
-#include "gd/shim/iconnectability.h"
-#include "gd/shim/icontroller.h"
-#include "gd/shim/idiscoverability.h"
-#include "gd/shim/ihci_layer.h"
-#include "gd/shim/iinquiry.h"
-#include "gd/shim/il2cap.h"
-#include "gd/shim/iname.h"
-#include "gd/shim/ipage.h"
-#include "gd/shim/iscanning.h"
-#include "gd/shim/istack.h"
+namespace bluetooth {
+namespace shim {
+class Dumpsys;
+class L2cap;
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/shim/page.cc b/gd/shim/page.cc
deleted file mode 100644
index aa79d29..0000000
--- a/gd/shim/page.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "bt_gd_shim"
-
-#include <functional>
-#include <memory>
-
-#include "common/bidi_queue.h"
-#include "hci/address.h"
-#include "hci/controller.h"
-#include "hci/hci_packets.h"
-#include "module.h"
-#include "neighbor/page.h"
-#include "neighbor/scan_parameters.h"
-#include "os/handler.h"
-#include "os/log.h"
-#include "shim/page.h"
-
-namespace bluetooth {
-namespace shim {
-
-const ModuleFactory Page::Factory = ModuleFactory([]() { return new Page(); });
-
-struct Page::impl {
-  impl(neighbor::PageModule* module);
-  ~impl();
-
-  neighbor::PageModule* module_{nullptr};
-};
-
-Page::impl::impl(neighbor::PageModule* module) : module_(module) {}
-
-Page::impl::~impl() {}
-
-void Page::SetScanActivity(uint16_t interval, uint16_t window) {
-  neighbor::ScanParameters params{.interval = static_cast<neighbor::ScanInterval>(interval),
-                                  .window = static_cast<neighbor::ScanWindow>(window)};
-  return pimpl_->module_->SetScanActivity(params);
-}
-
-void Page::GetScanActivity(uint16_t& interval, uint16_t& window) const {
-  neighbor::ScanParameters params = pimpl_->module_->GetScanActivity();
-
-  interval = static_cast<uint16_t>(params.interval);
-  window = static_cast<uint16_t>(params.window);
-}
-
-void Page::SetInterlacedScan() {
-  return pimpl_->module_->SetInterlacedScan();
-}
-void Page::SetStandardScan() {
-  return pimpl_->module_->SetStandardScan();
-}
-
-/**
- * Module methods
- */
-void Page::ListDependencies(ModuleList* list) {
-  list->add<neighbor::PageModule>();
-}
-
-void Page::Start() {
-  pimpl_ = std::make_unique<impl>(GetDependency<neighbor::PageModule>());
-}
-
-void Page::Stop() {
-  pimpl_.reset();
-}
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/page.h b/gd/shim/page.h
deleted file mode 100644
index b4e71cd..0000000
--- a/gd/shim/page.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <memory>
-
-#include "module.h"
-#include "shim/ipage.h"
-
-namespace bluetooth {
-namespace shim {
-
-class Page : public bluetooth::Module, public bluetooth::shim::IPage {
- public:
-  void SetScanActivity(uint16_t interval, uint16_t window) override;
-  void GetScanActivity(uint16_t& interval, uint16_t& window) const override;
-
-  void SetInterlacedScan() override;
-  void SetStandardScan() override;
-
-  Page() = default;
-  ~Page() = default;
-
-  static const ModuleFactory Factory;
-
- protected:
-  void ListDependencies(ModuleList* list) override;  // Module
-  void Start() override;                             // Module
-  void Stop() override;                              // Module
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-  DISALLOW_COPY_AND_ASSIGN(Page);
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/scanning.cc b/gd/shim/scanning.cc
deleted file mode 100644
index 17d3e22..0000000
--- a/gd/shim/scanning.cc
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "bt_gd_shim"
-
-#include <functional>
-#include <memory>
-
-#include "common/bind.h"
-#include "hci/address.h"
-#include "hci/hci_packets.h"
-#include "hci/le_report.h"
-#include "hci/le_scanning_manager.h"
-#include "module.h"
-#include "os/handler.h"
-#include "os/log.h"
-#include "shim/scanning.h"
-
-namespace bluetooth {
-namespace shim {
-
-constexpr size_t kAdvertisingReportBufferSize = 1024;
-
-struct Scanning::impl : public hci::LeScanningManagerCallbacks {
-  void StartScanning(bool set_active, AdvertisingReportCallback advertising_callback,
-                     DirectedAdvertisingReportCallback directed_advertising_callback,
-                     ExtendedAdvertisingReportCallback extended_advertising_callback,
-                     ScanningTimeoutCallback timeout_callback);
-  void StopScanning();
-
-  void on_advertisements(std::vector<std::shared_ptr<hci::LeReport>>) override;
-  void on_timeout() override;
-  os::Handler* Handler() override;
-
-  void OnStopped();
-
-  impl(hci::LeScanningManager* scanning_manager, os::Handler* handler);
-  ~impl();
-
- private:
-  hci::LeScanningManager* scanning_manager_{nullptr};
-  os::Handler* handler_;
-  bool active_scanning_{true};
-
-  AdvertisingReportCallback advertising_callback_;
-  DirectedAdvertisingReportCallback directed_advertising_callback_;
-  ExtendedAdvertisingReportCallback extended_advertising_callback_;
-  ScanningTimeoutCallback timeout_callback_;
-};
-
-const ModuleFactory Scanning::Factory = ModuleFactory([]() { return new Scanning(); });
-
-Scanning::impl::impl(hci::LeScanningManager* scanning_manager, os::Handler* handler)
-    : scanning_manager_(scanning_manager), handler_(handler) {}
-
-Scanning::impl::~impl() {}
-
-struct ExtendedEventTypeOptions {
-  bool connectable{false};
-  bool scannable{false};
-  bool directed{false};
-  bool scan_response{false};
-  bool legacy{false};
-  bool continuing{false};
-  bool truncated{false};
-};
-
-constexpr uint16_t kBleEventConnectableBit = (0x0001 << 0);   // BLE_EVT_CONNECTABLE_BIT
-constexpr uint16_t kBleEventScannableBit = (0x0001 << 1);     // BLE_EVT_SCANNABLE_BIT
-constexpr uint16_t kBleEventDirectedBit = (0x0001 << 2);      // BLE_EVT_DIRECTED_BIT
-constexpr uint16_t kBleEventScanResponseBit = (0x0001 << 3);  // BLE_EVT_SCAN_RESPONSE_BIT
-constexpr uint16_t kBleEventLegacyBit = (0x0001 << 4);        // BLE_EVT_LEGACY_BIT
-constexpr uint16_t kBleEventIncompleteContinuing = (0x0001 << 5);
-constexpr uint16_t kBleEventIncompleteTruncated = (0x0001 << 6);
-
-static void TransformToExtendedEventType(uint16_t* extended_event_type, ExtendedEventTypeOptions o) {
-  ASSERT(extended_event_type != nullptr);
-  *extended_event_type = (o.connectable ? kBleEventConnectableBit : 0) | (o.scannable ? kBleEventScannableBit : 0) |
-                         (o.directed ? kBleEventDirectedBit : 0) | (o.scan_response ? kBleEventScanResponseBit : 0) |
-                         (o.legacy ? kBleEventLegacyBit : 0) | (o.continuing ? kBleEventIncompleteContinuing : 0) |
-                         (o.truncated ? kBleEventIncompleteTruncated : 0);
-}
-
-void Scanning::impl::on_advertisements(std::vector<std::shared_ptr<hci::LeReport>> reports) {
-  for (auto le_report : reports) {
-    AdvertisingReport report{
-        .string_address = le_report->address_.ToString(),
-        .address_type = static_cast<uint8_t>(le_report->address_type_),
-        .rssi = le_report->rssi_,
-        .extended_event_type = 0,
-        .data = nullptr,
-        .len = 0,
-    };
-
-    uint8_t advertising_data_buffer[kAdvertisingReportBufferSize];
-    // Copy gap data, if any, into temporary buffer as payload for legacy stack.
-    if (!le_report->gap_data_.empty()) {
-      bzero(advertising_data_buffer, kAdvertisingReportBufferSize);
-      uint8_t* p = advertising_data_buffer;
-      for (auto gap_data : le_report->gap_data_) {
-        *p++ = gap_data.data_.size() + sizeof(gap_data.data_type_);
-        *p++ = static_cast<uint8_t>(gap_data.data_type_);
-        p = (uint8_t*)memcpy(p, &gap_data.data_[0], gap_data.data_.size()) + gap_data.data_.size();
-      }
-      report.data = advertising_data_buffer;
-      report.len = p - report.data;
-    }
-
-    switch (le_report->GetReportType()) {
-      case hci::LeReport::ReportType::ADVERTISING_EVENT:
-        switch (le_report->advertising_event_type_) {
-          case hci::AdvertisingEventType::ADV_IND:
-            TransformToExtendedEventType(&report.extended_event_type,
-                                         {.connectable = true, .scannable = true, .legacy = true});
-            break;
-          case hci::AdvertisingEventType::ADV_DIRECT_IND:
-            TransformToExtendedEventType(&report.extended_event_type,
-                                         {.connectable = true, .directed = true, .legacy = true});
-            break;
-          case hci::AdvertisingEventType::ADV_SCAN_IND:
-            TransformToExtendedEventType(&report.extended_event_type, {.scannable = true, .legacy = true});
-            break;
-          case hci::AdvertisingEventType::ADV_NONCONN_IND:
-            TransformToExtendedEventType(&report.extended_event_type, {.legacy = true});
-            break;
-          case hci::AdvertisingEventType::ADV_DIRECT_IND_LOW:  // SCAN_RESPONSE
-            TransformToExtendedEventType(
-                &report.extended_event_type,
-                {.connectable = true, .scannable = true, .scan_response = true, .legacy = true});
-            break;
-          default:
-            LOG_WARN("%s Unsupported event type:%s", __func__,
-                     AdvertisingEventTypeText(le_report->advertising_event_type_).c_str());
-            return;
-        }
-        if (!advertising_callback_) {
-          LOG_INFO("Discarding advertising packet after scan stopped");
-        } else {
-          advertising_callback_(report);
-        }
-        break;
-
-      case hci::LeReport::ReportType::DIRECTED_ADVERTISING_EVENT: {
-        DirectedAdvertisingReport directed_report(report);
-        std::shared_ptr<hci::DirectedLeReport> directed_le_report =
-            std::static_pointer_cast<hci::DirectedLeReport>(le_report);
-        directed_report.directed_advertising_type = static_cast<uint8_t>(directed_le_report->direct_address_type_);
-        if (!directed_advertising_callback_) {
-          LOG_INFO("Discarding directed advertising packet after scan stopped");
-        } else {
-          directed_advertising_callback_(directed_report);
-        }
-      } break;
-
-      case hci::LeReport::ReportType::EXTENDED_ADVERTISING_EVENT: {
-        ExtendedAdvertisingReport extended_report(report);
-        std::shared_ptr<hci::ExtendedLeReport> extended_le_report =
-            std::static_pointer_cast<hci::ExtendedLeReport>(le_report);
-        TransformToExtendedEventType(&report.extended_event_type, {.connectable = extended_le_report->connectable_,
-                                                                   .scannable = extended_le_report->scannable_,
-                                                                   .directed = extended_le_report->directed_,
-                                                                   .scan_response = extended_le_report->scan_response_,
-                                                                   .legacy = false,
-                                                                   .continuing = !extended_le_report->complete_,
-                                                                   .truncated = extended_le_report->truncated_});
-        if (!extended_advertising_callback_) {
-          LOG_INFO("Discarding extended advertising packet after scan stopped");
-        } else {
-          extended_advertising_callback_(extended_report);
-        }
-      } break;
-    }
-  }
-}
-
-void Scanning::impl::on_timeout() {
-  timeout_callback_();
-}
-
-os::Handler* Scanning::impl::Handler() {
-  return handler_;
-}
-
-void Scanning::impl::StartScanning(bool set_active, AdvertisingReportCallback advertising_callback,
-                                   DirectedAdvertisingReportCallback directed_advertising_callback,
-                                   ExtendedAdvertisingReportCallback extended_advertising_callback,
-                                   ScanningTimeoutCallback timeout_callback) {
-  active_scanning_ = set_active;
-  advertising_callback_ = advertising_callback;
-  directed_advertising_callback_ = directed_advertising_callback;
-  extended_advertising_callback_ = extended_advertising_callback;
-  timeout_callback_ = timeout_callback;
-
-  scanning_manager_->StartScan(this);
-  LOG_DEBUG("%s Started le %s scanning", __func__, (active_scanning_) ? "active" : "passive");
-}
-
-void Scanning::impl::StopScanning() {
-  LOG_DEBUG("%s Stopping le %s scanning", __func__, (active_scanning_) ? "active" : "passive");
-  scanning_manager_->StopScan(common::Bind(&impl::OnStopped, common::Unretained(this)));
-  advertising_callback_ = {};
-  directed_advertising_callback_ = {};
-  extended_advertising_callback_ = {};
-  timeout_callback_ = {};
-}
-
-void Scanning::impl::OnStopped() {
-  LOG_DEBUG("%s Stopped le %s scanning", __func__, (active_scanning_) ? "active" : "passive");
-}
-
-void Scanning::StartScanning(bool set_active, AdvertisingReportCallback advertising_callback,
-                             DirectedAdvertisingReportCallback directed_advertising_callback,
-                             ExtendedAdvertisingReportCallback extended_advertising_callback,
-                             ScanningTimeoutCallback timeout_callback) {
-  pimpl_->StartScanning(set_active, advertising_callback, directed_advertising_callback, extended_advertising_callback,
-                        timeout_callback);
-}
-
-void Scanning::StopScanning() {
-  pimpl_->StopScanning();
-}
-
-/**
- * Module methods
- */
-void Scanning::ListDependencies(ModuleList* list) {
-  list->add<hci::LeScanningManager>();
-}
-
-void Scanning::Start() {
-  pimpl_ = std::make_unique<impl>(GetDependency<hci::LeScanningManager>(), GetHandler());
-}
-
-void Scanning::Stop() {
-  pimpl_.reset();
-}
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/scanning.h b/gd/shim/scanning.h
deleted file mode 100644
index b19ca34..0000000
--- a/gd/shim/scanning.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <memory>
-#include <string>
-
-#include "module.h"
-#include "shim/iscanning.h"
-
-namespace bluetooth {
-namespace shim {
-
-class Scanning : public bluetooth::Module, public bluetooth::shim::IScanning {
- public:
-  Scanning() = default;
-  ~Scanning() = default;
-
-  void StartScanning(bool set_active, AdvertisingReportCallback advertising_callback,
-                     DirectedAdvertisingReportCallback directed_advertisting_callback,
-                     ExtendedAdvertisingReportCallback extended_advertising_callback,
-                     ScanningTimeoutCallback timeout_callback) override;
-  void StopScanning() override;
-
-  static const ModuleFactory Factory;
-
- protected:
-  void ListDependencies(ModuleList* list) override;  // Module
-  void Start() override;                             // Module
-  void Stop() override;                              // Module
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-  DISALLOW_COPY_AND_ASSIGN(Scanning);
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/stack.cc b/gd/shim/stack.cc
deleted file mode 100644
index 7822a35..0000000
--- a/gd/shim/stack.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "bt_gd_shim"
-
-#include "shim/stack.h"
-#include "hal/hci_hal.h"
-#include "hci/acl_manager.h"
-#include "hci/classic_security_manager.h"
-#include "hci/le_advertising_manager.h"
-#include "hci/le_scanning_manager.h"
-#include "l2cap/classic/l2cap_classic_module.h"
-#include "l2cap/le/l2cap_le_module.h"
-#include "neighbor/connectability.h"
-#include "neighbor/discoverability.h"
-#include "neighbor/inquiry.h"
-#include "neighbor/name.h"
-#include "neighbor/page.h"
-#include "neighbor/scan.h"
-#include "os/log.h"
-#include "os/thread.h"
-#include "security/security_module.h"
-#include "shim/advertising.h"
-#include "shim/connectability.h"
-#include "shim/controller.h"
-#include "shim/discoverability.h"
-#include "shim/hci_layer.h"
-#include "shim/inquiry.h"
-#include "shim/l2cap.h"
-#include "shim/name.h"
-#include "shim/page.h"
-#include "shim/scanning.h"
-#include "stack_manager.h"
-
-using ::bluetooth::os::Thread;
-
-struct bluetooth::shim::Stack::impl {
-  void Start() {
-    if (is_running_) {
-      LOG_ERROR("%s Gd stack already running", __func__);
-      return;
-    }
-
-    LOG_INFO("%s Starting Gd stack", __func__);
-    ModuleList modules;
-    modules.add<::bluetooth::hal::HciHal>();
-    modules.add<::bluetooth::hci::AclManager>();
-    modules.add<::bluetooth::hci::LeAdvertisingManager>();
-    modules.add<::bluetooth::hci::LeScanningManager>();
-    modules.add<::bluetooth::l2cap::classic::L2capClassicModule>();
-    modules.add<::bluetooth::l2cap::le::L2capLeModule>();
-    modules.add<::bluetooth::neighbor::ConnectabilityModule>();
-    modules.add<::bluetooth::neighbor::DiscoverabilityModule>();
-    modules.add<::bluetooth::neighbor::InquiryModule>();
-    modules.add<::bluetooth::neighbor::NameModule>();
-    modules.add<::bluetooth::neighbor::PageModule>();
-    modules.add<::bluetooth::neighbor::ScanModule>();
-    modules.add<::bluetooth::shim::Controller>();
-    modules.add<::bluetooth::shim::HciLayer>();
-    modules.add<::bluetooth::security::SecurityModule>();
-    modules.add<::bluetooth::shim::Advertising>();
-    modules.add<::bluetooth::shim::Connectability>();
-    modules.add<::bluetooth::shim::Discoverability>();
-    modules.add<::bluetooth::shim::Inquiry>();
-    modules.add<::bluetooth::shim::Name>();
-    modules.add<::bluetooth::shim::L2cap>();
-    modules.add<::bluetooth::shim::Page>();
-    modules.add<::bluetooth::shim::Scanning>();
-
-    stack_thread_ = new Thread("gd_stack_thread", Thread::Priority::NORMAL);
-    stack_manager_.StartUp(&modules, stack_thread_);
-    // TODO(cmanton) Gd stack has spun up another thread with no
-    // ability to ascertain the completion
-    is_running_ = true;
-    LOG_INFO("%s Successfully toggled Gd stack", __func__);
-  }
-
-  void Stop() {
-    if (!is_running_) {
-      LOG_ERROR("%s Gd stack not running", __func__);
-      return;
-    }
-
-    stack_manager_.ShutDown();
-    delete stack_thread_;
-    is_running_ = false;
-    LOG_INFO("%s Successfully shut down Gd stack", __func__);
-  }
-
-  IAdvertising* GetAdvertising() {
-    return stack_manager_.GetInstance<bluetooth::shim::Advertising>();
-  }
-
-  IController* GetController() {
-    return stack_manager_.GetInstance<bluetooth::shim::Controller>();
-  }
-
-  IConnectability* GetConnectability() {
-    return stack_manager_.GetInstance<bluetooth::shim::Connectability>();
-  }
-
-  IDiscoverability* GetDiscoverability() {
-    return stack_manager_.GetInstance<bluetooth::shim::Discoverability>();
-  }
-
-  IHciLayer* GetHciLayer() {
-    return stack_manager_.GetInstance<bluetooth::shim::HciLayer>();
-  }
-
-  IInquiry* GetInquiry() {
-    return stack_manager_.GetInstance<bluetooth::shim::Inquiry>();
-  }
-
-  IL2cap* GetL2cap() {
-    return stack_manager_.GetInstance<bluetooth::shim::L2cap>();
-  }
-
-  IName* GetName() {
-    return stack_manager_.GetInstance<bluetooth::shim::Name>();
-  }
-
-  IPage* GetPage() {
-    return stack_manager_.GetInstance<bluetooth::shim::Page>();
-  }
-
-  IScanning* GetScanning() {
-    return stack_manager_.GetInstance<bluetooth::shim::Scanning>();
-  }
-
- private:
-  os::Thread* stack_thread_ = nullptr;
-  bool is_running_ = false;
-  StackManager stack_manager_;
-};
-
-bluetooth::shim::Stack::Stack() {
-  pimpl_ = std::make_unique<impl>();
-  LOG_INFO("%s Created gd stack", __func__);
-}
-
-void bluetooth::shim::Stack::Start() {
-  pimpl_->Start();
-}
-
-void bluetooth::shim::Stack::Stop() {
-  pimpl_->Stop();
-}
-
-bluetooth::shim::IAdvertising* bluetooth::shim::Stack::GetAdvertising() {
-  return pimpl_->GetAdvertising();
-}
-
-bluetooth::shim::IConnectability* bluetooth::shim::Stack::GetConnectability() {
-  return pimpl_->GetConnectability();
-}
-
-bluetooth::shim::IController* bluetooth::shim::Stack::GetController() {
-  return pimpl_->GetController();
-}
-
-bluetooth::shim::IDiscoverability* bluetooth::shim::Stack::GetDiscoverability() {
-  return pimpl_->GetDiscoverability();
-}
-
-bluetooth::shim::IHciLayer* bluetooth::shim::Stack::GetHciLayer() {
-  return pimpl_->GetHciLayer();
-}
-
-bluetooth::shim::IInquiry* bluetooth::shim::Stack::GetInquiry() {
-  return pimpl_->GetInquiry();
-}
-
-bluetooth::shim::IL2cap* bluetooth::shim::Stack::GetL2cap() {
-  return pimpl_->GetL2cap();
-}
-
-bluetooth::shim::IName* bluetooth::shim::Stack::GetName() {
-  return pimpl_->GetName();
-}
-
-bluetooth::shim::IPage* bluetooth::shim::Stack::GetPage() {
-  return pimpl_->GetPage();
-}
-
-bluetooth::shim::IScanning* bluetooth::shim::Stack::GetScanning() {
-  return pimpl_->GetScanning();
-}
-
-bluetooth::shim::IStack* bluetooth::shim::GetGabeldorscheStack() {
-  static IStack* instance = new Stack();
-  return instance;
-}
diff --git a/gd/shim/stack.h b/gd/shim/stack.h
deleted file mode 100644
index f4a51fc..0000000
--- a/gd/shim/stack.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-
-#include "shim/iadvertising.h"
-#include "shim/iconnectability.h"
-#include "shim/icontroller.h"
-#include "shim/idiscoverability.h"
-#include "shim/ihci_layer.h"
-#include "shim/iinquiry.h"
-#include "shim/il2cap.h"
-#include "shim/iname.h"
-#include "shim/ipage.h"
-#include "shim/iscanning.h"
-#include "shim/istack.h"
-
-/**
- * The shim layer implementation on the Gd stack side.
- */
-namespace bluetooth {
-namespace shim {
-
-class Stack : public IStack {
- public:
-  Stack();
-  ~Stack() = default;
-
-  void Start() override;  // IStack
-  void Stop() override;   // IStack
-
-  IAdvertising* GetAdvertising() override;  // IStack
-  IController* GetController() override;  // IStack
-  IConnectability* GetConnectability() override;  // IStack
-  IHciLayer* GetHciLayer() override;      // IStack
-  IDiscoverability* GetDiscoverability() override;  // IStack
-  IInquiry* GetInquiry() override;                  // IStack
-  IName* GetName() override;                        // IStack
-  IL2cap* GetL2cap() override;                      // IStack
-  IPage* GetPage() override;                        // IStack
-  IScanning* GetScanning() override;                // IStack
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-
-  Stack(const Stack&) = delete;
-  void operator=(const Stack&) = delete;
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/gd/shim/test_gen/dumpsys_data.bfbs b/gd/shim/test_gen/dumpsys_data.bfbs
new file mode 100644
index 0000000..afe2ea5
--- /dev/null
+++ b/gd/shim/test_gen/dumpsys_data.bfbs
Binary files differ
diff --git a/gd/shim/test_gen/dumpsys_test_data_bin b/gd/shim/test_gen/dumpsys_test_data_bin
new file mode 100644
index 0000000..b9c410d
--- /dev/null
+++ b/gd/shim/test_gen/dumpsys_test_data_bin
Binary files differ
diff --git a/gd/shim/test_gen/dumpsys_test_data_bin.h b/gd/shim/test_gen/dumpsys_test_data_bin.h
new file mode 100644
index 0000000..1a30dd2
--- /dev/null
+++ b/gd/shim/test_gen/dumpsys_test_data_bin.h
@@ -0,0 +1,67 @@
+// Generated file by bluetooth_flatbuffer bundler
+#pragma once
+#include <sys/types.h>
+#include <string>
+namespace bluetooth {
+namespace test {
+extern const unsigned char* data;
+extern const size_t data_size;
+const std::string& GetBundledSchemaData();
+}  // namespace test
+}  // namespace bluetooth
+namespace {
+const unsigned char data_[] = {
+    0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x00,
+    0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x44, 0x00,
+    0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x62, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x2e, 0x44, 0x75, 0x6d,
+    0x70, 0x73, 0x79, 0x73, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x42, 0x75, 0x6e, 0x64,
+    0x6c, 0x65, 0x64, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x00, 0x00,
+    0x00, 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00, 0x04, 0x00,
+    0x00, 0x00, 0xe0, 0x02, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x42, 0x46, 0x42, 0x53, 0x10, 0x00, 0x1c, 0x00, 0x04,
+    0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 0x38,
+    0x01, 0x00, 0x00, 0x3c, 0xfe, 0xff, 0xff, 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00,
+    0x00, 0x62, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x79, 0x73, 0x44,
+    0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x06, 0x00, 0x0c,
+    0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x82, 0xff, 0xff, 0xff,
+    0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f,
+    0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
+    0x14, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+    0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x74, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00,
+    0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x41, 0x6e, 0x79, 0x00, 0x07, 0x00, 0x00, 0x00, 0x70, 0x72,
+    0x69, 0x76, 0x61, 0x63, 0x79, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x73, 0x68, 0x69, 0x6d,
+    0x5f, 0x64, 0x75, 0x6d, 0x70, 0x73, 0x79, 0x73, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10,
+    0x00, 0x08, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00,
+    0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb6, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x00, 0x74,
+    0x69, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x70, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x62, 0x6c, 0x75,
+    0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x2e, 0x73, 0x68, 0x69, 0x6d, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x79, 0x73,
+    0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x68, 0xff, 0xff, 0xff, 0x00,
+    0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x04, 0x00, 0x00, 0x00, 0x50, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+    0x00, 0x41, 0x6e, 0x79, 0x00, 0x07, 0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x00, 0x46, 0xff,
+    0xff, 0xff, 0x00, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x00, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x0c,
+    0x00, 0x10, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+    0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00,
+    0x00, 0x62, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x55, 0x6e,
+    0x69, 0x74, 0x54, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x14, 0x00, 0x08,
+    0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+    0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00,
+    0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x41, 0x6e, 0x79, 0x00, 0x07,
+    0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x00, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x00, 0x00,
+    0x00, 0x15, 0x00, 0x00, 0x00, 0x62, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x2e, 0x44, 0x75, 0x6d, 0x70,
+    0x73, 0x79, 0x73, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00};
+const std::string string_data_(data_, data_ + sizeof(data_));
+}  // namespace
+const unsigned char* bluetooth::test::data = data_;
+const size_t bluetooth::test::data_size = 884;
+const std::string& bluetooth::test::GetBundledSchemaData() {
+  return string_data_;
+}
diff --git a/gd/stack_manager.cc b/gd/stack_manager.cc
index 9c01d56..06a7ba2 100644
--- a/gd/stack_manager.cc
+++ b/gd/stack_manager.cc
@@ -57,11 +57,11 @@
   auto future = promise.get_future();
   handler_->Post(common::BindOnce(&StackManager::handle_shut_down, common::Unretained(this), std::move(promise)));
 
-  auto stop_status = future.wait_for(std::chrono::seconds(3));
+  auto stop_status = future.wait_for(std::chrono::seconds(5));
   ASSERT_LOG(stop_status == std::future_status::ready, "Can't stop stack");
 
   handler_->Clear();
-  handler_->WaitUntilStopped(std::chrono::milliseconds(20));
+  handler_->WaitUntilStopped(std::chrono::milliseconds(2000));
   delete handler_;
   delete management_thread_;
 }
diff --git a/gd/storage/Android.bp b/gd/storage/Android.bp
new file mode 100644
index 0000000..2024a7e
--- /dev/null
+++ b/gd/storage/Android.bp
@@ -0,0 +1,22 @@
+filegroup {
+    name: "BluetoothStorageSources",
+    srcs: [
+            "config_cache.cc",
+            "config_cache_helper.cc",
+            "legacy_config_file.cc",
+            "mutation_entry.cc",
+            "storage_module.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothStorageTestSources",
+    srcs: [
+            "config_cache_test.cc",
+            "config_cache_helper_test.cc",
+            "legacy_config_file_test.cc",
+            "mutation_test.cc",
+            "storage_module_test.cc",
+    ],
+}
+
diff --git a/gd/storage/config_cache.cc b/gd/storage/config_cache.cc
new file mode 100644
index 0000000..5927cf5
--- /dev/null
+++ b/gd/storage/config_cache.cc
@@ -0,0 +1,310 @@
+/*
+ * 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.
+ */
+
+#include "storage/config_cache.h"
+
+#include <ios>
+#include <sstream>
+#include <utility>
+
+#include "storage/mutation.h"
+
+namespace {
+
+bool TrimAfterNewLine(std::string& value) {
+  std::string value_no_newline;
+  size_t newline_position = value.find_first_of('\n');
+  if (newline_position != std::string::npos) {
+    value.erase(newline_position);
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+namespace bluetooth {
+namespace storage {
+
+const std::unordered_set<std::string_view> kLinkKeyPropertyNames = {
+    "LinkKey", "LE_KEY_PENC", "LE_KEY_PID", "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"};
+
+const std::string ConfigCache::kDefaultSectionName = "Global";
+
+ConfigCache::ConfigCache(size_t temp_device_capacity)
+    : information_sections_(), persistent_devices_(), temporary_devices_(temp_device_capacity) {}
+
+void ConfigCache::SetPersistentConfigChangedCallback(std::function<void()> persistent_config_changed_callback) {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  persistent_config_changed_callback_ = std::move(persistent_config_changed_callback);
+}
+
+ConfigCache::ConfigCache(ConfigCache&& other) noexcept
+    : persistent_config_changed_callback_(std::move(other.persistent_config_changed_callback_)),
+      information_sections_(std::move(other.information_sections_)),
+      persistent_devices_(std::move(other.persistent_devices_)),
+      temporary_devices_(std::move(other.temporary_devices_)) {
+  // std::function will be in a valid but unspecified state after std::move(), hence resetting it
+  other.persistent_config_changed_callback_ = {};
+}
+
+ConfigCache& ConfigCache::operator=(ConfigCache&& other) noexcept {
+  if (&other == this) {
+    return *this;
+  }
+  std::lock_guard<std::recursive_mutex> my_lock(mutex_);
+  std::lock_guard<std::recursive_mutex> others_lock(other.mutex_);
+  persistent_config_changed_callback_.swap(other.persistent_config_changed_callback_);
+  other.persistent_config_changed_callback_ = {};
+  information_sections_ = std::move(other.information_sections_);
+  persistent_devices_ = std::move(other.persistent_devices_);
+  temporary_devices_ = std::move(other.temporary_devices_);
+  return *this;
+}
+
+bool ConfigCache::operator==(const ConfigCache& rhs) const {
+  std::lock_guard<std::recursive_mutex> my_lock(mutex_);
+  std::lock_guard<std::recursive_mutex> others_lock(rhs.mutex_);
+  return information_sections_ == rhs.information_sections_ && persistent_devices_ == rhs.persistent_devices_ &&
+         temporary_devices_ == rhs.temporary_devices_;
+}
+
+bool ConfigCache::operator!=(const ConfigCache& rhs) const {
+  return !(*this == rhs);
+}
+
+void ConfigCache::Clear() {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  if (information_sections_.size() > 0) {
+    information_sections_.clear();
+    PersistentConfigChangedCallback();
+  }
+  if (persistent_devices_.size() > 0) {
+    persistent_devices_.clear();
+    PersistentConfigChangedCallback();
+  }
+  if (temporary_devices_.size() > 0) {
+    temporary_devices_.clear();
+  }
+}
+
+bool ConfigCache::HasSection(const std::string& section) const {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  return information_sections_.contains(section) || persistent_devices_.contains(section) ||
+         temporary_devices_.contains(section);
+}
+
+bool ConfigCache::HasProperty(const std::string& section, const std::string& property) const {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  auto section_iter = information_sections_.find(section);
+  if (section_iter != information_sections_.end()) {
+    return section_iter->second.find(property) != section_iter->second.end();
+  }
+  section_iter = persistent_devices_.find(section);
+  if (section_iter != persistent_devices_.end()) {
+    return section_iter->second.find(property) != section_iter->second.end();
+  }
+  section_iter = temporary_devices_.find(section);
+  if (section_iter != temporary_devices_.end()) {
+    return section_iter->second.find(property) != section_iter->second.end();
+  }
+  return false;
+}
+
+std::optional<std::string> ConfigCache::GetProperty(const std::string& section, const std::string& property) const {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  auto section_iter = information_sections_.find(section);
+  if (section_iter != information_sections_.end()) {
+    auto property_iter = section_iter->second.find(property);
+    if (property_iter != section_iter->second.end()) {
+      return property_iter->second;
+    }
+  }
+  section_iter = persistent_devices_.find(section);
+  if (section_iter != persistent_devices_.end()) {
+    auto property_iter = section_iter->second.find(property);
+    if (property_iter != section_iter->second.end()) {
+      return property_iter->second;
+    }
+  }
+  section_iter = temporary_devices_.find(section);
+  if (section_iter != temporary_devices_.end()) {
+    auto property_iter = section_iter->second.find(property);
+    if (property_iter != section_iter->second.end()) {
+      return property_iter->second;
+    }
+  }
+  return std::nullopt;
+}
+
+void ConfigCache::SetProperty(std::string section, std::string property, std::string value) {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  if (TrimAfterNewLine(section) || TrimAfterNewLine(property) || TrimAfterNewLine(value)) {
+    android_errorWriteLog(0x534e4554, "70808273");
+  }
+  ASSERT_LOG(!section.empty(), "Empty section name not allowed");
+  ASSERT_LOG(!property.empty(), "Empty property name not allowed");
+  if (!IsDeviceSection(section)) {
+    auto section_iter = information_sections_.find(section);
+    if (section_iter == information_sections_.end()) {
+      section_iter = information_sections_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
+    }
+    section_iter->second.insert_or_assign(property, std::move(value));
+    PersistentConfigChangedCallback();
+    return;
+  }
+  auto section_iter = persistent_devices_.find(section);
+  if (section_iter == persistent_devices_.end() && IsLinkKeyProperty(property)) {
+    // move paired devices or create new paired device when a link key is set
+    auto section_properties = temporary_devices_.extract(section);
+    if (section_properties) {
+      section_iter = persistent_devices_.try_emplace_back(section, std::move(section_properties->second)).first;
+    } else {
+      section_iter = persistent_devices_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
+    }
+  }
+  if (section_iter != persistent_devices_.end()) {
+    section_iter->second.insert_or_assign(property, std::move(value));
+    PersistentConfigChangedCallback();
+    return;
+  }
+  section_iter = temporary_devices_.find(section);
+  if (section_iter == temporary_devices_.end()) {
+    auto triple = temporary_devices_.try_emplace(section, common::ListMap<std::string, std::string>{});
+    section_iter = std::get<0>(triple);
+  }
+  section_iter->second.insert_or_assign(property, std::move(value));
+}
+
+bool ConfigCache::RemoveSection(const std::string& section) {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  // sections are unique among all three maps, hence removing from one of them is enough
+  if (information_sections_.extract(section) || persistent_devices_.extract(section)) {
+    PersistentConfigChangedCallback();
+    return true;
+  } else {
+    return temporary_devices_.extract(section).has_value();
+  }
+}
+
+bool ConfigCache::RemoveProperty(const std::string& section, const std::string& property) {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  auto section_iter = information_sections_.find(section);
+  if (section_iter != information_sections_.end()) {
+    return section_iter->second.extract(property).has_value();
+  }
+  section_iter = persistent_devices_.find(section);
+  if (section_iter != persistent_devices_.end()) {
+    auto value = section_iter->second.extract(property);
+    if (value && IsLinkKeyProperty(property)) {
+      // move unpaired device
+      auto section_properties = persistent_devices_.extract(section);
+      temporary_devices_.insert_or_assign(section, std::move(section_properties->second));
+    }
+    if (value.has_value()) {
+      PersistentConfigChangedCallback();
+      return true;
+    } else {
+      return false;
+    }
+  }
+  section_iter = temporary_devices_.find(section);
+  if (section_iter != temporary_devices_.end()) {
+    return section_iter->second.extract(property).has_value();
+  }
+  return false;
+}
+
+bool ConfigCache::IsDeviceSection(const std::string& section) {
+  return hci::Address::IsValidAddress(section);
+}
+
+bool ConfigCache::IsLinkKeyProperty(const std::string& property) {
+  return kLinkKeyPropertyNames.find(property) != kLinkKeyPropertyNames.end();
+}
+
+void ConfigCache::RemoveSectionWithProperty(const std::string& property) {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  size_t num_persistent_removed = 0;
+  for (auto* config_section : {&information_sections_, &persistent_devices_}) {
+    for (auto it = config_section->begin(); it != config_section->end();) {
+      if (it->second.contains(property)) {
+        LOG_DEBUG("Removing persistent section %s with property %s", it->first.c_str(), property.c_str());
+        it = config_section->erase(it);
+        num_persistent_removed++;
+        continue;
+      }
+      it++;
+    }
+  }
+  for (auto it = temporary_devices_.begin(); it != temporary_devices_.end();) {
+    if (it->second.contains(property)) {
+      LOG_DEBUG("Removing temporary section %s with property %s", it->first.c_str(), property.c_str());
+      it = temporary_devices_.erase(it);
+      continue;
+    }
+    it++;
+  }
+  if (num_persistent_removed > 0) {
+    PersistentConfigChangedCallback();
+  }
+}
+
+std::vector<std::string> ConfigCache::GetPersistentDevices() const {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  std::vector<std::string> paired_devices;
+  paired_devices.reserve(persistent_devices_.size());
+  for (const auto& elem : persistent_devices_) {
+    paired_devices.emplace_back(elem.first);
+  }
+  return paired_devices;
+}
+
+void ConfigCache::Commit(Mutation& mutation) {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  auto& entries = mutation.entries_;
+  while (!entries.empty()) {
+    auto entry = std::move(entries.front());
+    entries.pop();
+    if (entry.is_add) {
+      SetProperty(std::move(entry.section), std::move(entry.property), std::move(entry.value));
+    } else {
+      if (entry.value.empty()) {
+        RemoveSection(entry.section);
+      } else {
+        RemoveProperty(entry.section, entry.property);
+      }
+    }
+  }
+}
+
+std::string ConfigCache::SerializeToLegacyFormat() const {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  std::stringstream serialized;
+  for (const auto* config_section : {&information_sections_, &persistent_devices_}) {
+    for (const auto& section : *config_section) {
+      serialized << "[" << section.first << "]" << std::endl;
+      for (const auto& property : section.second) {
+        serialized << property.first << " = " << property.second << std::endl;
+      }
+      serialized << std::endl;
+    }
+  }
+  return serialized.str();
+}
+
+}  // namespace storage
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/storage/config_cache.h b/gd/storage/config_cache.h
new file mode 100644
index 0000000..0fc950a
--- /dev/null
+++ b/gd/storage/config_cache.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <functional>
+#include <list>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "common/list_map.h"
+#include "common/lru_cache.h"
+#include "hci/address.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace storage {
+
+class Mutation;
+
+// A memory operated section-key-value structured config
+//
+// This class is thread safe
+class ConfigCache {
+ public:
+  explicit ConfigCache(size_t temp_device_capacity);
+  virtual ~ConfigCache() = default;
+
+  // no copy
+  DISALLOW_COPY_AND_ASSIGN(ConfigCache);
+
+  // can move
+  ConfigCache(ConfigCache&& other) noexcept;
+  ConfigCache& operator=(ConfigCache&& other) noexcept;
+
+  // comparison operators, callback doesn't count
+  bool operator==(const ConfigCache& rhs) const;
+  bool operator!=(const ConfigCache& rhs) const;
+
+  // observers
+  virtual bool HasSection(const std::string& section) const;
+  virtual bool HasProperty(const std::string& section, const std::string& property) const;
+  // Get property, return std::nullopt if section or property does not exist
+  virtual std::optional<std::string> GetProperty(const std::string& section, const std::string& property) const;
+  // Returns a copy of persistent device MAC addresses
+  virtual std::vector<std::string> GetPersistentDevices() const;
+  // Serialize to legacy config format
+  virtual std::string SerializeToLegacyFormat() const;
+
+  // modifiers
+  // Commit all mutation entries in sequence while holding the config mutex
+  virtual void Commit(Mutation& mutation);
+  virtual void SetProperty(std::string section, std::string property, std::string value);
+  virtual bool RemoveSection(const std::string& section);
+  virtual bool RemoveProperty(const std::string& section, const std::string& property);
+  // TODO: have a systematic way of doing this instead of specialized methods
+  // Remove sections with |property| set
+  virtual void RemoveSectionWithProperty(const std::string& property);
+  // remove all content in this config cache, restore it to the state after the explicit constructor
+  virtual void Clear();
+  // Set a callback to notify interested party that a persistent config change has just happened
+  virtual void SetPersistentConfigChangedCallback(std::function<void()> persistent_config_changed_callback);
+
+  // static methods
+  // Check if section is formatted as a MAC address
+  static bool IsDeviceSection(const std::string& section);
+  // Check if property represent one of those link keys used for paired devices
+  static bool IsLinkKeyProperty(const std::string& property);
+
+  // constants
+  static const std::string kDefaultSectionName;
+
+ private:
+  mutable std::recursive_mutex mutex_;
+  // A callback to notify interested party that a persistent config change has just happened, empty by default
+  std::function<void()> persistent_config_changed_callback_;
+  // Common section that does not relate to remote device, will be written to disk
+  common::ListMap<std::string, common::ListMap<std::string, std::string>> information_sections_;
+  // Information about persistent devices, normally paired, will be written to disk
+  common::ListMap<std::string, common::ListMap<std::string, std::string>> persistent_devices_;
+  // Information about temporary devices, normally unpaired, will not be written to disk, will be evicted automatically
+  // if capacity exceeds given value during initialization
+  common::LruCache<std::string, common::ListMap<std::string, std::string>> temporary_devices_;
+
+  // Convenience method to check if the callback is valid before calling it
+  inline void PersistentConfigChangedCallback() const {
+    if (persistent_config_changed_callback_) {
+      persistent_config_changed_callback_();
+    }
+  }
+};
+
+}  // namespace storage
+}  // namespace bluetooth
diff --git a/gd/storage/config_cache_helper.cc b/gd/storage/config_cache_helper.cc
new file mode 100644
index 0000000..6a4185b
--- /dev/null
+++ b/gd/storage/config_cache_helper.cc
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#include "storage/config_cache_helper.h"
+#include "common/strings.h"
+
+namespace bluetooth {
+namespace storage {
+
+void ConfigCacheHelper::SetBool(const std::string& section, const std::string& property, bool value) {
+  config_cache_.SetProperty(section, property, value ? "true" : "false");
+}
+
+std::optional<bool> ConfigCacheHelper::GetBool(const std::string& section, const std::string& property) const {
+  auto value_str = config_cache_.GetProperty(section, property);
+  if (!value_str) {
+    return std::nullopt;
+  }
+  if (*value_str == "true") {
+    return true;
+  } else if (*value_str == "false") {
+    return false;
+  } else {
+    return std::nullopt;
+  }
+}
+
+void ConfigCacheHelper::SetUint64(const std::string& section, const std::string& property, uint64_t value) {
+  config_cache_.SetProperty(section, property, std::to_string(value));
+}
+
+std::optional<uint64_t> ConfigCacheHelper::GetUint64(const std::string& section, const std::string& property) const {
+  auto value_str = config_cache_.GetProperty(section, property);
+  if (!value_str) {
+    return std::nullopt;
+  }
+  std::size_t pos = 0;
+  uint64_t value = std::stoull(*value_str, &pos);
+  if (pos != value_str->size()) {
+    return std::nullopt;
+  }
+  return value;
+}
+
+void ConfigCacheHelper::SetInt(const std::string& section, const std::string& property, int value) {
+  config_cache_.SetProperty(section, property, std::to_string(value));
+}
+
+std::optional<int> ConfigCacheHelper::GetInt(const std::string& section, const std::string& property) const {
+  auto value_str = config_cache_.GetProperty(section, property);
+  if (!value_str) {
+    return std::nullopt;
+  }
+  std::size_t pos = 0;
+  int value = std::stoi(*value_str, &pos);
+  if (pos != value_str->size()) {
+    return std::nullopt;
+  }
+  return value;
+}
+
+void ConfigCacheHelper::SetBin(
+    const std::string& section, const std::string& property, const std::vector<uint8_t>& value) {
+  config_cache_.SetProperty(section, property, common::ToHexString(value));
+}
+
+std::optional<std::vector<uint8_t>> ConfigCacheHelper::GetBin(
+    const std::string& section, const std::string& property) const {
+  auto value_str = config_cache_.GetProperty(section, property);
+  if (!value_str) {
+    return std::nullopt;
+  }
+  auto value = common::FromHexString(*value_str);
+  if (!value) {
+    LOG_WARN("value_str cannot be parsed to std::vector<uint8_t>");
+  }
+  return value;
+}
+
+}  // namespace storage
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/storage/config_cache_helper.h b/gd/storage/config_cache_helper.h
new file mode 100644
index 0000000..fd938af
--- /dev/null
+++ b/gd/storage/config_cache_helper.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include "storage/config_cache.h"
+
+namespace bluetooth {
+namespace storage {
+
+// A thin wrapper around ConfigCache and implement more type supports other than std::string
+//
+// - all SetX methods accept value as copy and std::move() in encouraged
+// - all GetX methods return std::optional<X> and std::nullopt if not exist. std::optional<> can be treated as bool
+class ConfigCacheHelper {
+ public:
+  static ConfigCacheHelper FromConfigCache(ConfigCache& config_cache) {
+    return ConfigCacheHelper(config_cache);
+  }
+  explicit ConfigCacheHelper(ConfigCache& config_cache) : config_cache_(config_cache) {}
+  virtual ~ConfigCacheHelper() = default;
+  virtual void SetBool(const std::string& section, const std::string& property, bool value);
+  virtual std::optional<bool> GetBool(const std::string& section, const std::string& property) const;
+  virtual void SetUint64(const std::string& section, const std::string& property, uint64_t value);
+  virtual std::optional<uint64_t> GetUint64(const std::string& section, const std::string& property) const;
+  virtual void SetInt(const std::string& section, const std::string& property, int value);
+  virtual std::optional<int> GetInt(const std::string& section, const std::string& property) const;
+  virtual void SetBin(const std::string& section, const std::string& property, const std::vector<uint8_t>& value);
+  virtual std::optional<std::vector<uint8_t>> GetBin(const std::string& section, const std::string& property) const;
+
+ private:
+  ConfigCache& config_cache_;
+};
+
+}  // namespace storage
+}  // namespace bluetooth
diff --git a/gd/storage/config_cache_helper_test.cc b/gd/storage/config_cache_helper_test.cc
new file mode 100644
index 0000000..0b46442
--- /dev/null
+++ b/gd/storage/config_cache_helper_test.cc
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#include <limits>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "storage/config_cache_helper.h"
+
+namespace testing {
+
+using bluetooth::storage::ConfigCache;
+using bluetooth::storage::ConfigCacheHelper;
+
+TEST(ConfigCacheHelperTest, set_get_bool_test) {
+  ConfigCache config(100);
+  // true
+  ConfigCacheHelper(config).SetBool("A", "B", true);
+  EXPECT_EQ(*config.GetProperty("A", "B"), "true");
+  auto res = ConfigCacheHelper(config).GetBool("A", "B");
+  EXPECT_TRUE(res);
+  EXPECT_TRUE(*res);
+  // false
+  ConfigCacheHelper(config).SetBool("A", "B", false);
+  EXPECT_EQ(*config.GetProperty("A", "B"), "false");
+  res = ConfigCacheHelper(config).GetBool("A", "B");
+  EXPECT_TRUE(res);
+  EXPECT_FALSE(*res);
+}
+
+TEST(ConfigCacheHelperTest, set_get_uint64_test) {
+  ConfigCache config(100);
+  // small
+  ConfigCacheHelper(config).SetUint64("A", "B", 123);
+  EXPECT_EQ(*config.GetProperty("A", "B"), "123");
+  EXPECT_EQ(*ConfigCacheHelper(config).GetUint64("A", "B"), uint64_t(123));
+  // big
+  uint64_t num = std::numeric_limits<int>::max();
+  num = num * 10;
+  ConfigCacheHelper(config).SetUint64("A", "B", num);
+  EXPECT_EQ(*config.GetProperty("A", "B"), std::to_string(std::numeric_limits<int>::max()) + "0");
+  EXPECT_EQ(*ConfigCacheHelper(config).GetUint64("A", "B"), num);
+  // zero
+  ConfigCacheHelper(config).SetUint64("A", "B", 0);
+  EXPECT_EQ(*config.GetProperty("A", "B"), "0");
+  EXPECT_EQ(*ConfigCacheHelper(config).GetUint64("A", "B"), 0);
+}
+
+TEST(ConfigCacheHelperTest, set_get_int_test) {
+  ConfigCache config(100);
+  // positive
+  ConfigCacheHelper(config).SetInt("A", "B", 123);
+  EXPECT_EQ(*config.GetProperty("A", "B"), "123");
+  EXPECT_EQ(*ConfigCacheHelper(config).GetInt("A", "B"), int(123));
+  // negative
+  ConfigCacheHelper(config).SetInt("A", "B", -123);
+  EXPECT_EQ(*config.GetProperty("A", "B"), "-123");
+  EXPECT_EQ(*ConfigCacheHelper(config).GetInt("A", "B"), int(-123));
+  // zero
+  ConfigCacheHelper(config).SetInt("A", "B", 0);
+  EXPECT_EQ(*config.GetProperty("A", "B"), "0");
+  EXPECT_EQ(*ConfigCacheHelper(config).GetInt("A", "B"), int(0));
+}
+
+TEST(ConfigCacheHelperTest, set_get_bin_test) {
+  ConfigCache config(100);
+  // empty
+  std::vector<uint8_t> data;
+  ConfigCacheHelper(config).SetBin("A", "B", data);
+  EXPECT_EQ(*config.GetProperty("A", "B"), "");
+  EXPECT_THAT(*ConfigCacheHelper(config).GetBin("A", "B"), ContainerEq(data));
+  // non-empty
+  std::vector<uint8_t> data2 = {0xAB, 0x5D, 0x42};
+  ConfigCacheHelper(config).SetBin("A", "B", data2);
+  EXPECT_EQ(*config.GetProperty("A", "B"), "ab5d42");
+  EXPECT_THAT(*ConfigCacheHelper(config).GetBin("A", "B"), ContainerEq(data2));
+}
+
+}  // namespace testing
\ No newline at end of file
diff --git a/gd/storage/config_cache_test.cc b/gd/storage/config_cache_test.cc
new file mode 100644
index 0000000..6c5e1e5
--- /dev/null
+++ b/gd/storage/config_cache_test.cc
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+
+#include "storage/config_cache.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <cstdio>
+
+namespace testing {
+
+namespace {
+std::string GetTestAddress(int i) {
+  std::string res = "00:00:00:00:00:00";
+  res.reserve(res.size() + 1);
+  std::snprintf(res.data(), res.capacity(), "AA:BB:CC:DD:EE:%02d", i);
+  return res;
+}
+}  // namespace
+
+using bluetooth::storage::ConfigCache;
+
+TEST(ConfigCacheTest, simple_set_get_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  auto value = config.GetProperty("A", "B");
+  EXPECT_TRUE(value);
+  EXPECT_EQ(*value, "C");
+}
+
+TEST(ConfigCacheTest, empty_values_test) {
+  ConfigCache config(100);
+  EXPECT_DEATH({ config.SetProperty("", "B", "C"); }, "Empty section name not allowed");
+  EXPECT_DEATH({ config.SetProperty("A", "", "C"); }, "Empty property name not allowed");
+  // empty value is allowed
+  config.SetProperty("A", "B", "");
+  auto value = config.GetProperty("A", "B");
+  EXPECT_TRUE(value);
+  EXPECT_EQ(*value, "");
+}
+
+TEST(ConfigCacheTest, insert_boundary_device_with_linkkey_test) {
+  ConfigCache config(2);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("CC:DD:EE:FF:00:10", "Name", "Hello");
+  config.SetProperty("CC:DD:EE:FF:00:09", "Name", "Hello 2");
+  config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  EXPECT_TRUE(config.GetProperty("CC:DD:EE:FF:00:10", "Name"));
+}
+
+TEST(ConfigCacheTest, comparison_test) {
+  ConfigCache config_1(2);
+  config_1.SetProperty("A", "B", "C");
+  config_1.SetProperty("CC:DD:EE:FF:00:10", "Name", "Hello");
+  config_1.SetProperty("CC:DD:EE:FF:00:09", "Name", "Hello 2");
+  config_1.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  ConfigCache config_2(2);
+  config_2.SetProperty("A", "B", "C");
+  config_2.SetProperty("CC:DD:EE:FF:00:10", "Name", "Hello");
+  config_2.SetProperty("CC:DD:EE:FF:00:09", "Name", "Hello 2");
+  config_2.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  EXPECT_EQ(config_1, config_2);
+  // Config with different temp device order should not be equal
+  EXPECT_TRUE(config_2.GetProperty("CC:DD:EE:FF:00:10", "Name"));
+  EXPECT_NE(config_1, config_2);
+  EXPECT_TRUE(config_1.GetProperty("CC:DD:EE:FF:00:10", "Name"));
+  EXPECT_EQ(config_1, config_2);
+  // Config with different persistent device order should not be equal
+  config_1.SetProperty("CC:DD:EE:FF:00:12", "LinkKey", "AABBAABBCCDDEE");
+  config_2.RemoveSection("CC:DD:EE:FF:00:11");
+  config_2.SetProperty("CC:DD:EE:FF:00:12", "LinkKey", "AABBAABBCCDDEE");
+  config_2.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  EXPECT_NE(config_1, config_2);
+  // Config with different capacity should not be equal
+  ConfigCache config_3(3);
+  config_3.SetProperty("A", "B", "C");
+  config_3.SetProperty("CC:DD:EE:FF:00:10", "Name", "Hello");
+  config_3.SetProperty("CC:DD:EE:FF:00:09", "Name", "Hello 2");
+  config_3.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  config_3.SetProperty("CC:DD:EE:FF:00:12", "LinkKey", "AABBAABBCCDDEE");
+  EXPECT_NE(config_1, config_3);
+  // Empty config should not be equal to non-empty ones
+  ConfigCache config_4(2);
+  EXPECT_NE(config_1, config_4);
+  // Empty configs should be equal
+  ConfigCache config_5(2);
+  EXPECT_EQ(config_4, config_5);
+  // Empty configs with different capacity should not be equal
+  ConfigCache config_6(3);
+  EXPECT_NE(config_4, config_6);
+}
+
+TEST(ConfigCacheTest, empty_string_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "");
+  auto value = config.GetProperty("A", "B");
+  EXPECT_TRUE(value);
+  EXPECT_EQ(*value, "");
+}
+
+TEST(ConfigCacheTest, mac_address_set_get_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  auto value = config.GetProperty("A", "B");
+  EXPECT_TRUE(value);
+  EXPECT_EQ(*value, "C");
+  value = config.GetProperty("AA:BB:CC:DD:EE:FF", "B");
+  EXPECT_TRUE(value);
+  EXPECT_EQ(*value, "C");
+  EXPECT_FALSE(config.GetProperty("A", "BC"));
+  EXPECT_FALSE(config.GetProperty("ABC", "B"));
+}
+
+TEST(ConfigCacheTest, has_section_and_property_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  EXPECT_TRUE(config.HasSection("A"));
+  EXPECT_TRUE(config.HasSection("AA:BB:CC:DD:EE:FF"));
+  EXPECT_TRUE(config.HasProperty("A", "B"));
+  EXPECT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  auto value = config.GetProperty("AA:BB:CC:DD:EE:FF", "C");
+  EXPECT_TRUE(value);
+  EXPECT_EQ(*value, "D");
+  value = config.GetProperty("AA:BB:CC:DD:EE:FF", "B");
+  EXPECT_TRUE(value);
+  EXPECT_EQ(*value, "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "E");
+  value = config.GetProperty("AA:BB:CC:DD:EE:FF", "B");
+  EXPECT_TRUE(value);
+  EXPECT_THAT(value, Optional(StrEq("E")));
+  EXPECT_FALSE(config.HasSection("Ab"));
+  EXPECT_FALSE(config.HasSection("AA:11:CC:DD:EE:FF"));
+  EXPECT_FALSE(config.HasProperty("A", "bB"));
+  EXPECT_FALSE(config.HasProperty("AA:BB:11:DD:EE:FF", "B"));
+}
+
+TEST(ConfigCacheTest, remove_section_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  EXPECT_TRUE(config.HasSection("A"));
+  EXPECT_TRUE(config.HasSection("AA:BB:CC:DD:EE:FF"));
+  EXPECT_TRUE(config.HasProperty("A", "B"));
+  EXPECT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
+  EXPECT_TRUE(config.RemoveSection("AA:BB:CC:DD:EE:FF"));
+  EXPECT_TRUE(config.RemoveSection("A"));
+  EXPECT_FALSE(config.HasProperty("A", "B"));
+  EXPECT_FALSE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
+}
+
+TEST(ConfigCacheTest, remove_property_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  EXPECT_TRUE(config.HasSection("A"));
+  EXPECT_TRUE(config.HasSection("AA:BB:CC:DD:EE:FF"));
+  EXPECT_TRUE(config.HasProperty("A", "B"));
+  EXPECT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
+  EXPECT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "C"));
+  EXPECT_TRUE(config.RemoveProperty("AA:BB:CC:DD:EE:FF", "B"));
+  EXPECT_FALSE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
+  EXPECT_FALSE(config.GetProperty("AA:BB:CC:DD:EE:FF", "B"));
+}
+
+TEST(ConfigCacheTest, remove_all_properties_from_section_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  EXPECT_TRUE(config.HasSection("A"));
+  EXPECT_TRUE(config.HasSection("AA:BB:CC:DD:EE:FF"));
+  EXPECT_TRUE(config.HasProperty("A", "B"));
+  EXPECT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
+  EXPECT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "C"));
+  EXPECT_TRUE(config.RemoveSection("AA:BB:CC:DD:EE:FF"));
+  EXPECT_FALSE(config.HasSection("AA:BB:CC:DD:EE:FF"));
+  EXPECT_FALSE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
+  EXPECT_FALSE(config.GetProperty("AA:BB:CC:DD:EE:FF", "C"));
+}
+
+TEST(ConfigCacheTest, get_persistent_devices_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  EXPECT_TRUE(config.HasProperty("CC:DD:EE:FF:00:11", "LinkKey"));
+  EXPECT_THAT(config.GetPersistentDevices(), ElementsAre("CC:DD:EE:FF:00:11"));
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "LinkKey", "DEERDEERDEER");
+  EXPECT_THAT(config.GetPersistentDevices(), ElementsAre("CC:DD:EE:FF:00:11", "AA:BB:CC:DD:EE:FF"));
+  EXPECT_TRUE(config.RemoveProperty("CC:DD:EE:FF:00:11", "LinkKey"));
+  EXPECT_THAT(config.GetPersistentDevices(), ElementsAre("AA:BB:CC:DD:EE:FF"));
+}
+
+TEST(ConfigCacheTest, appoaching_temporary_config_limit_test) {
+  ConfigCache config(2);
+  for (int i = 0; i < 10; ++i) {
+    config.SetProperty(GetTestAddress(i), "Name", "Hello" + std::to_string(i));
+    if (i % 2 == 0) {
+      config.SetProperty(GetTestAddress(i), "LinkKey", "Key" + std::to_string(i));
+    }
+  }
+  for (int i = 0; i < 10; ++i) {
+    if (i % 2 == 0) {
+      EXPECT_TRUE(config.HasSection(GetTestAddress(i)));
+      EXPECT_TRUE(config.HasProperty(GetTestAddress(i), "LinkKey"));
+      EXPECT_THAT(config.GetProperty(GetTestAddress(i), "Name"), Optional(StrEq("Hello" + std::to_string(i))));
+    } else if (i >= 7) {
+      EXPECT_TRUE(config.HasSection(GetTestAddress(i)));
+      EXPECT_THAT(config.GetProperty(GetTestAddress(i), "Name"), Optional(StrEq("Hello" + std::to_string(i))));
+    } else {
+      EXPECT_FALSE(config.HasSection(GetTestAddress(i)));
+    }
+  }
+  EXPECT_THAT(
+      config.GetPersistentDevices(),
+      ElementsAre(GetTestAddress(0), GetTestAddress(2), GetTestAddress(4), GetTestAddress(6), GetTestAddress(8)));
+}
+
+TEST(ConfigCacheTest, remove_section_with_property_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  config.SetProperty("CC:DD:EE:FF:00:11", "B", "AABBAABBCCDDEE");
+  config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  config.RemoveSectionWithProperty("B");
+  EXPECT_FALSE(config.HasSection("A"));
+  EXPECT_FALSE(config.HasSection("AA:BB:CC:DD:EE:FF"));
+  EXPECT_FALSE(config.HasSection("CC:DD:EE:FF:00:11"));
+}
+
+TEST(ConfigCacheTest, persistent_config_changed_callback_test) {
+  ConfigCache config(100);
+  int num_change = 0;
+  config.SetPersistentConfigChangedCallback([&num_change] { num_change++; });
+  config.SetProperty("A", "B", "C");
+  EXPECT_EQ(num_change, 1);
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  EXPECT_EQ(num_change, 1);
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  EXPECT_EQ(num_change, 1);
+  config.SetProperty("CC:DD:EE:FF:00:11", "B", "AABBAABBCCDDEE");
+  EXPECT_EQ(num_change, 1);
+  config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  EXPECT_EQ(num_change, 2);
+  config.RemoveProperty("CC:DD:EE:FF:00:11", "LinkKey");
+  EXPECT_EQ(num_change, 3);
+  config.RemoveSectionWithProperty("B");
+  EXPECT_EQ(num_change, 4);
+}
+
+}  // namespace testing
\ No newline at end of file
diff --git a/gd/storage/legacy_config_file.cc b/gd/storage/legacy_config_file.cc
new file mode 100644
index 0000000..752260b
--- /dev/null
+++ b/gd/storage/legacy_config_file.cc
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include "storage/legacy_config_file.h"
+
+#include <cerrno>
+#include <fstream>
+#include <sstream>
+
+#include "common/strings.h"
+#include "os/files.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace storage {
+
+LegacyConfigFile::LegacyConfigFile(std::string path) : path_(std::move(path)) {
+  ASSERT(!path_.empty());
+};
+
+std::optional<ConfigCache> LegacyConfigFile::Read(size_t temp_devices_capacity) {
+  ASSERT(!path_.empty());
+  std::ifstream config_file(path_);
+  if (!config_file || !config_file.is_open()) {
+    LOG_ERROR("unable to open file '%s', error: %s", path_.c_str(), strerror(errno));
+    return std::nullopt;
+  }
+  int line_num = 0;
+  ConfigCache cache(temp_devices_capacity);
+  std::string line;
+  std::string section(ConfigCache::kDefaultSectionName);
+  while (std::getline(config_file, line)) {
+    ++line_num;
+    line = common::StringTrim(std::move(line));
+    if (line.front() == '\0' || line.front() == '#') {
+      continue;
+    }
+    if (line.front() == '[') {
+      if (line.back() != ']') {
+        LOG_WARN("unterminated section name on line %d", line_num);
+        return std::nullopt;
+      }
+      // Read 'test' from '[text]', hence -2
+      section = line.substr(1, line.size() - 2);
+    } else {
+      auto tokens = common::StringSplit(line, "=", 2);
+      if (tokens.size() != 2) {
+        LOG_WARN("no key/value separator found on line %d", line_num);
+        return std::nullopt;
+      }
+      tokens[0] = common::StringTrim(std::move(tokens[0]));
+      tokens[1] = common::StringTrim(std::move(tokens[1]));
+      cache.SetProperty(section, tokens[0], std::move(tokens[1]));
+    }
+  }
+  return cache;
+}
+
+bool LegacyConfigFile::Write(const ConfigCache& cache) {
+  return os::WriteToFile(path_, cache.SerializeToLegacyFormat());
+}
+
+bool LegacyConfigFile::Delete() {
+  if (remove(path_.c_str()) != 0) {
+    LOG_WARN("unable to remove file '%s', error: %s", path_.c_str(), strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+}  // namespace storage
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/storage/legacy_config_file.h b/gd/storage/legacy_config_file.h
new file mode 100644
index 0000000..2a319c9
--- /dev/null
+++ b/gd/storage/legacy_config_file.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <string>
+#include <utility>
+
+#include "storage/config_cache.h"
+
+namespace bluetooth {
+namespace storage {
+
+// similar to INI
+class LegacyConfigFile {
+ public:
+  static LegacyConfigFile FromPath(std::string path) {
+    return LegacyConfigFile(std::move(path));
+  }
+  explicit LegacyConfigFile(std::string path);
+  std::optional<ConfigCache> Read(size_t temp_devices_capacity);
+  bool Write(const ConfigCache& cache);
+  bool Delete();
+
+ private:
+  std::string path_;
+};
+
+}  // namespace storage
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/storage/legacy_config_file_test.cc b/gd/storage/legacy_config_file_test.cc
new file mode 100644
index 0000000..a97d83a
--- /dev/null
+++ b/gd/storage/legacy_config_file_test.cc
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#include "storage/legacy_config_file.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <filesystem>
+
+#include "os/files.h"
+
+namespace testing {
+
+using bluetooth::os::ReadSmallFile;
+using bluetooth::os::WriteToFile;
+using bluetooth::storage::ConfigCache;
+using bluetooth::storage::LegacyConfigFile;
+
+TEST(LegacyConfigFileTest, write_and_read_loop_back_test) {
+  auto temp_dir = std::filesystem::temp_directory_path();
+  auto temp_config = temp_dir / "temp_config.txt";
+
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  EXPECT_TRUE(config.HasProperty("CC:DD:EE:FF:00:11", "LinkKey"));
+  EXPECT_THAT(config.GetPersistentDevices(), ElementsAre("CC:DD:EE:FF:00:11"));
+
+  EXPECT_TRUE(LegacyConfigFile::FromPath(temp_config.string()).Write(config));
+  auto config_read = LegacyConfigFile::FromPath(temp_config.string()).Read(100);
+  EXPECT_TRUE(config_read);
+  // Unpaired devices do not exist in persistent config file
+  config.RemoveSection("AA:BB:CC:DD:EE:FF");
+  EXPECT_EQ(config, *config_read);
+  EXPECT_THAT(config_read->GetPersistentDevices(), ElementsAre("CC:DD:EE:FF:00:11"));
+  EXPECT_THAT(config_read->GetProperty("A", "B"), Optional(StrEq("C")));
+  EXPECT_THAT(config_read->GetProperty("CC:DD:EE:FF:00:11", "LinkKey"), Optional(StrEq("AABBAABBCCDDEE")));
+
+  EXPECT_TRUE(std::filesystem::remove(temp_config));
+}
+
+static const std::string kReadTestConfig =
+    "[Info]\n"
+    "FileSource = Empty\n"
+    "TimeCreated = 2020-05-20 01:20:56\n"
+    "\n"
+    "[Metrics]\n"
+    "Salt256Bit = 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n"
+    "\n"
+    "[Adapter]\n"
+    "Address = 01:02:03:ab:cd:ef\n"
+    "LE_LOCAL_KEY_IRK = fedcba0987654321fedcba0987654321\n"
+    "LE_LOCAL_KEY_IR = fedcba0987654321fedcba0987654322\n"
+    "LE_LOCAL_KEY_DHK = fedcba0987654321fedcba0987654323\n"
+    "LE_LOCAL_KEY_ER = fedcba0987654321fedcba0987654324\n"
+    "ScanMode = 2\n"
+    "DiscoveryTimeout = 120\n"
+    "\n"
+    "[01:02:03:ab:cd:ea]\n"
+    "name = hello world\n"
+    "LinkKey = fedcba0987654321fedcba0987654328\n";
+
+TEST(LegacyConfigFileTest, read_test) {
+  auto temp_dir = std::filesystem::temp_directory_path();
+  auto temp_config = temp_dir / "temp_config.txt";
+  EXPECT_TRUE(WriteToFile(temp_config.string(), kReadTestConfig));
+
+  auto config_read = LegacyConfigFile::FromPath(temp_config.string()).Read(100);
+  EXPECT_TRUE(config_read);
+  EXPECT_THAT(config_read->GetPersistentDevices(), ElementsAre("01:02:03:ab:cd:ea"));
+  EXPECT_THAT(config_read->GetProperty("Info", "FileSource"), Optional(StrEq("Empty")));
+  EXPECT_THAT(config_read->GetProperty("Info", "FileSource"), Optional(StrEq("Empty")));
+  EXPECT_THAT(
+      config_read->GetProperty("01:02:03:ab:cd:ea", "LinkKey"), Optional(StrEq("fedcba0987654321fedcba0987654328")));
+
+  EXPECT_TRUE(std::filesystem::remove(temp_config));
+}
+
+static const std::string kWriteTestConfig =
+    "[Info]\n"
+    "FileSource = Empty\n"
+    "TimeCreated = \n"
+    "\n"
+    "[Adapter]\n"
+    "Address = 01:02:03:ab:cd:ef\n"
+    "\n"
+    "[01:02:03:ab:cd:ea]\n"
+    "name = hello world\n"
+    "LinkKey = fedcba0987654321fedcba0987654328\n"
+    "\n";
+
+TEST(LegacyConfigFileTest, write_test) {
+  auto temp_dir = std::filesystem::temp_directory_path();
+  auto temp_config = temp_dir / "temp_config.txt";
+
+  ConfigCache config(100);
+  config.SetProperty("Info", "FileSource", "Empty");
+  config.SetProperty("Info", "TimeCreated", "");
+  config.SetProperty("Adapter", "Address", "01:02:03:ab:cd:ef");
+  config.SetProperty("01:02:03:ab:cd:ea", "name", "hello world");
+  config.SetProperty("01:02:03:ab:cd:ea", "LinkKey", "fedcba0987654321fedcba0987654328");
+  EXPECT_TRUE(LegacyConfigFile::FromPath(temp_config.string()).Write(config));
+
+  EXPECT_THAT(ReadSmallFile(temp_config.string()), Optional(StrEq(kWriteTestConfig)));
+
+  EXPECT_TRUE(std::filesystem::remove(temp_config));
+}
+
+static const std::string kConfigWithDuplicateSectionAndKey =
+    "                                                                                \n\
+first_key=value                                                                      \n\
+                                                                                     \n\
+# Device ID (DID) configuration                                                      \n\
+[DID]                                                                                \n\
+                                                                                     \n\
+# Record Number: 1, 2 or 3 - maximum of 3 records                                    \n\
+recordNumber = 1                                                                     \n\
+                                                                                     \n\
+# Primary Record - true or false (default)                                           \n\
+# There can be only one primary record                                               \n\
+primaryRecord = true                                                                 \n\
+                                                                                     \n\
+# Vendor ID '0xFFFF' indicates no Device ID Service Record is present in the device  \n\
+# 0x000F = Broadcom Corporation (default)                                            \n\
+#vendorId = 0x000F                                                                   \n\
+                                                                                     \n\
+# Vendor ID Source                                                                   \n\
+# 0x0001 = Bluetooth SIG assigned Device ID Vendor ID value (default)                \n\
+# 0x0002 = USB Implementer's Forum assigned Device ID Vendor ID value                \n\
+#vendorIdSource = 0x0001                                                             \n\
+                                                                                     \n\
+# Product ID & Product Version                                                       \n\
+# Per spec DID v1.3 0xJJMN for version is interpreted as JJ.M.N                      \n\
+# JJ: major version number, M: minor version number, N: sub-minor version number     \n\
+# For example: 1200, v14.3.6                                                         \n\
+productId = 0x1200                                                                   \n\
+version = 0x1111                                                                     \n\
+                                                                                     \n\
+# Optional attributes                                                                \n\
+#clientExecutableURL =                                                               \n\
+#serviceDescription =                                                                \n\
+#documentationURL =                                                                  \n\
+                                                                                     \n\
+# Additional optional DID records. Bluedroid supports up to 3 records.               \n\
+[DID]                                                                                \n\
+[DID]                                                                                \n\
+version = 0x1436                                                                     \n\
+                                                                                     \n\
+HiSyncId = 18446744073709551615                                                      \n\
+HiSyncId2 = 15001900                                                                 \n\
+";
+
+TEST(LegacyConfigFileTest, duplicate_section_and_key_test) {
+  auto temp_dir = std::filesystem::temp_directory_path();
+  auto temp_config = temp_dir / "temp_config.txt";
+  ASSERT_TRUE(WriteToFile(temp_config.string(), kConfigWithDuplicateSectionAndKey));
+
+  auto config_read = LegacyConfigFile::FromPath(temp_config.string()).Read(100);
+  ASSERT_TRUE(config_read);
+  EXPECT_THAT(config_read->GetProperty(ConfigCache::kDefaultSectionName, "first_key"), Optional(StrEq("value")));
+  // All sections with the same name merge into the same key-value pair
+  EXPECT_THAT(config_read->GetProperty("DID", "primaryRecord"), Optional(StrEq("true")));
+  // When keys are repeated, the later one wins
+  EXPECT_THAT(config_read->GetProperty("DID", "version"), Optional(StrEq("0x1436")));
+
+  EXPECT_TRUE(std::filesystem::remove(temp_config));
+}
+
+}  // namespace testing
\ No newline at end of file
diff --git a/gd/storage/mutation.h b/gd/storage/mutation.h
new file mode 100644
index 0000000..3c8ca8c
--- /dev/null
+++ b/gd/storage/mutation.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <queue>
+
+#include "storage/config_cache.h"
+#include "storage/mutation_entry.h"
+
+namespace bluetooth {
+namespace storage {
+
+class Mutation {
+ public:
+  explicit Mutation(ConfigCache& storage_module) : config_cache_(storage_module) {}
+
+  void Add(MutationEntry entry) {
+    entries_.emplace(std::move(entry));
+  }
+
+  void Commit() {
+    config_cache_.Commit(*this);
+  }
+
+  friend ConfigCache;
+
+ private:
+  ConfigCache& config_cache_;
+  std::queue<MutationEntry> entries_;
+};
+
+}  // namespace storage
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/storage/mutation_entry.cc b/gd/storage/mutation_entry.cc
new file mode 100644
index 0000000..f6516c0
--- /dev/null
+++ b/gd/storage/mutation_entry.cc
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#include "storage/mutation_entry.h"
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace storage {
+
+MutationEntry::MutationEntry(
+    bool is_add_param, std::string section_param, std::string property_param, std::string value_param)
+    : is_add(is_add_param),
+      section(std::move(section_param)),
+      property(std::move(property_param)),
+      value(std::move(value_param)) {
+  ASSERT_LOG(!section.empty(), "section cannot be empty any time");
+  if (is_add) {
+    ASSERT_LOG(!property.empty(), "property cannot be empty when is_add is true");
+    ASSERT_LOG(!value.empty(), "value cannot be empty when is_add is true");
+  }
+}
+
+}  // namespace storage
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/storage/mutation_entry.h b/gd/storage/mutation_entry.h
new file mode 100644
index 0000000..b638b57
--- /dev/null
+++ b/gd/storage/mutation_entry.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <string>
+
+namespace bluetooth {
+namespace storage {
+
+class MutationEntry {
+ public:
+  static MutationEntry Set(std::string section_param, std::string property_param, std::string value_param) {
+    return MutationEntry(true, std::move(section_param), std::move(property_param), std::move(value_param));
+  }
+
+  static MutationEntry Remove(std::string section_param) {
+    return MutationEntry(false, std::move(section_param));
+  }
+
+  static MutationEntry Remove(std::string section_param, std::string property_param) {
+    return MutationEntry(false, std::move(section_param), std::move(property_param));
+  }
+
+ private:
+  friend class ConfigCache;
+
+  MutationEntry(
+      bool is_add_param, std::string section_param, std::string property_param = "", std::string value_param = "");
+
+  bool is_add;
+  std::string section;
+  std::string property;
+  std::string value;
+};
+
+}  // namespace storage
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/storage/mutation_test.cc b/gd/storage/mutation_test.cc
new file mode 100644
index 0000000..5269b98
--- /dev/null
+++ b/gd/storage/mutation_test.cc
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#include "storage/mutation.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "storage/config_cache.h"
+
+namespace testing {
+
+using bluetooth::storage::ConfigCache;
+using bluetooth::storage::Mutation;
+using bluetooth::storage::MutationEntry;
+
+TEST(MutationTest, simple_sequence_test) {
+  ConfigCache config(100);
+  config.SetProperty("A", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
+  config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
+  config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
+  Mutation mutation(config);
+  mutation.Add(MutationEntry::Set("AA:BB:CC:DD:EE:FF", "LinkKey", "CCDDEEFFGG"));
+  mutation.Add(MutationEntry::Remove("AA:BB:CC:DD:EE:FF", "LinkKey"));
+  mutation.Commit();
+  EXPECT_THAT(config.GetPersistentDevices(), ElementsAre("CC:DD:EE:FF:00:11"));
+  Mutation mutation2(config);
+  mutation2.Add(MutationEntry::Set("AA:BB:CC:DD:EE:FF", "LinkKey", "CCDDEEFFGG"));
+  mutation2.Commit();
+  EXPECT_THAT(config.GetPersistentDevices(), ElementsAre("CC:DD:EE:FF:00:11", "AA:BB:CC:DD:EE:FF"));
+}
+
+}  // namespace testing
\ No newline at end of file
diff --git a/gd/storage/storage_module.cc b/gd/storage/storage_module.cc
new file mode 100644
index 0000000..fca9455
--- /dev/null
+++ b/gd/storage/storage_module.cc
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#include "storage/storage_module.h"
+
+#include <chrono>
+#include <ctime>
+#include <iomanip>
+#include <memory>
+
+#include "common/bind.h"
+#include "os/alarm.h"
+#include "os/files.h"
+#include "os/handler.h"
+#include "os/parameter_provider.h"
+#include "os/system_properties.h"
+#include "storage/config_cache.h"
+#include "storage/legacy_config_file.h"
+#include "storage/mutation.h"
+
+namespace bluetooth {
+namespace storage {
+
+using common::ListMap;
+using common::LruCache;
+using os::Alarm;
+using os::Handler;
+
+static const std::string kFactoryResetProperty = "persist.bluetooth.factoryreset";
+
+static const size_t kDefaultTempDeviceCapacity = 10000;
+// Save config whenever there is a change, but delay it by this value so that burst config change won't overwhelm disk
+static const std::chrono::milliseconds kDefaultConfigSaveDelay = std::chrono::milliseconds(3000);
+// Writing a config to disk takes a minimum 10 ms on a decent x86_64 machine, and 20 ms if including backup file
+// The config saving delay must be bigger than this value to avoid overwhelming the disk
+static const std::chrono::milliseconds kMinConfigSaveDelay = std::chrono::milliseconds(20);
+
+const std::string StorageModule::kInfoSection = "Info";
+const std::string StorageModule::kFileSourceProperty = "FileSource";
+const std::string StorageModule::kTimeCreatedProperty = "TimeCreated";
+const std::string StorageModule::kTimeCreatedFormat = "%Y-%m-%d %H:%M:%S";
+
+const std::string StorageModule::kAdapterSection = "Adapter";
+
+StorageModule::StorageModule(
+    std::string config_file_path,
+    std::chrono::milliseconds config_save_delay,
+    size_t temp_devices_capacity,
+    bool is_restricted_mode,
+    bool is_single_user_mode)
+    : config_file_path_(std::move(config_file_path)),
+      config_save_delay_(config_save_delay),
+      temp_devices_capacity_(temp_devices_capacity),
+      is_restricted_mode_(is_restricted_mode),
+      is_single_user_mode_(is_single_user_mode) {
+  // e.g. "/data/misc/bluedroid/bt_config.conf" to "/data/misc/bluedroid/bt_config.bak"
+  config_backup_path_ = config_file_path_.substr(0, config_file_path_.find_last_of('.')) + ".bak";
+  ASSERT_LOG(
+      config_save_delay > kMinConfigSaveDelay,
+      "Config save delay of %lld ms is not enought, must be at least %lld ms to avoid overwhelming the disk",
+      config_save_delay_.count(),
+      kMinConfigSaveDelay.count());
+};
+
+StorageModule::~StorageModule() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  pimpl_.reset();
+}
+
+const ModuleFactory StorageModule::Factory = ModuleFactory([]() {
+  return new StorageModule(
+      os::ParameterProvider::ConfigFilePath(), kDefaultConfigSaveDelay, kDefaultTempDeviceCapacity, false, false);
+});
+
+struct StorageModule::impl {
+  explicit impl(Handler* handler, ConfigCache cache) : config_save_alarm_(handler), cache_(std::move(cache)) {}
+  Alarm config_save_alarm_;
+  ConfigCache cache_;
+  bool has_pending_config_save_ = false;
+};
+
+Mutation StorageModule::Modify() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  return Mutation(pimpl_->cache_);
+}
+
+ConfigCache* StorageModule::GetConfigCache() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  return &pimpl_->cache_;
+}
+
+void StorageModule::SaveDelayed() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  if (pimpl_->has_pending_config_save_) {
+    return;
+  }
+  pimpl_->config_save_alarm_.Schedule(
+      common::BindOnce(&StorageModule::SaveImmediately, common::Unretained(this)), config_save_delay_);
+  pimpl_->has_pending_config_save_ = true;
+}
+
+void StorageModule::SaveImmediately() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  if (pimpl_->has_pending_config_save_) {
+    pimpl_->config_save_alarm_.Cancel();
+    pimpl_->has_pending_config_save_ = false;
+  }
+  // 1. rename old config to backup name
+  if (os::FileExists(config_file_path_)) {
+    ASSERT(os::RenameFile(config_file_path_, config_backup_path_));
+  }
+  // 2. write in-memory config to disk, if failed, backup can still be used
+  ASSERT(LegacyConfigFile::FromPath(config_file_path_).Write(pimpl_->cache_));
+  // 3. now write back up to disk as well
+  ASSERT(LegacyConfigFile::FromPath(config_backup_path_).Write(pimpl_->cache_));
+}
+
+void StorageModule::ListDependencies(ModuleList* list) {
+  // No dependencies
+}
+
+void StorageModule::Start() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  std::string file_source;
+  if (os::GetSystemProperty(kFactoryResetProperty) == "true") {
+    LegacyConfigFile::FromPath(config_file_path_).Delete();
+    LegacyConfigFile::FromPath(config_backup_path_).Delete();
+  }
+  auto config = LegacyConfigFile::FromPath(config_file_path_).Read(temp_devices_capacity_);
+  if (!config || !config->HasSection(kAdapterSection)) {
+    LOG_WARN("cannot load config at %s, using backup at %s.", config_file_path_.c_str(), config_backup_path_.c_str());
+    config = LegacyConfigFile::FromPath(config_backup_path_).Read(temp_devices_capacity_);
+    file_source = "Backup";
+  }
+  if (!config || !config->HasSection(kAdapterSection)) {
+    LOG_WARN("cannot load backup config at %s; creating new empty ones", config_backup_path_.c_str());
+    config.emplace(temp_devices_capacity_);
+    file_source = "Empty";
+  }
+  if (!file_source.empty()) {
+    config->SetProperty(kInfoSection, kFileSourceProperty, std::move(file_source));
+  }
+  // Cleanup temporary pairings if we have left guest mode
+  if (!is_restricted_mode_) {
+    config->RemoveSectionWithProperty("Restricted");
+  }
+  // Read or set config file creation timestamp
+  auto time_str = config->GetProperty(kInfoSection, kTimeCreatedProperty);
+  if (!time_str) {
+    std::stringstream ss;
+    auto now = std::chrono::system_clock::now();
+    auto now_time_t = std::chrono::system_clock::to_time_t(now);
+    ss << std::put_time(std::localtime(&now_time_t), kTimeCreatedFormat.c_str());
+    config->SetProperty(kInfoSection, kTimeCreatedProperty, ss.str());
+  }
+  config->SetPersistentConfigChangedCallback([this] { this->SaveDelayed(); });
+  // TODO (b/158035889) Migrate metrics module to GD
+  pimpl_ = std::make_unique<impl>(GetHandler(), std::move(config.value()));
+}
+
+void StorageModule::Stop() {
+  SaveImmediately();
+  std::lock_guard<std::mutex> lock(mutex_);
+  pimpl_.reset();
+}
+
+std::string StorageModule::ToString() const {
+  return "Storage Module";
+}
+
+}  // namespace storage
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/storage/storage_module.h b/gd/storage/storage_module.h
new file mode 100644
index 0000000..4a60a90
--- /dev/null
+++ b/gd/storage/storage_module.h
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <array>
+#include <chrono>
+#include <cstdint>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include "hci/address.h"
+#include "module.h"
+#include "storage/config_cache.h"
+#include "storage/mutation.h"
+
+namespace bluetooth {
+
+namespace shim {
+class BtifConfigInterface;
+}
+
+namespace storage {
+
+class StorageModule : public bluetooth::Module {
+ public:
+  static const std::string kInfoSection;
+  static const std::string kFileSourceProperty;
+  static const std::string kTimeCreatedProperty;
+  static const std::string kTimeCreatedFormat;
+
+  static const std::string kAdapterSection;
+
+  // Create the storage module where:
+  // - config_file_path is the path to the config file on disk, a .bak file will be created with the original
+  // - config_save_delay is the duration after which to dump config to disk after SaveDelayed() is called
+  // - temp_devices_capacity is the number of temporary, typically unpaired devices to hold in a memory based LRU
+  // - is_restricted_mode and is_single_user_mode are flags from upper layer
+  StorageModule(
+      std::string config_file_path,
+      std::chrono::milliseconds config_save_delay,
+      size_t temp_devices_capacity,
+      bool is_restricted_mode,
+      bool is_single_user_mode);
+  ~StorageModule() override;
+  static const ModuleFactory Factory;
+
+  // Modify the underlying config by starting a mutation. All entries in the mutation will be applied atomically when
+  // Commit() is called. User should never touch ConfigCache() directly.
+  Mutation Modify();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+  std::string ToString() const override;
+
+  friend shim::BtifConfigInterface;
+  // For shim layer only
+  ConfigCache* GetConfigCache();
+  // Normally, underlying config will be saved at most 3 seconds after the first config change in a series of changes
+  // This method triggers the delayed saving automatically, the delay is equal to |config_save_delay_|
+  void SaveDelayed();
+  // In some cases, one may want to save the config immediately to disk. Call this method with caution as it runs
+  // immediately on the calling thread
+  void SaveImmediately();
+
+ private:
+  struct impl;
+  mutable std::mutex mutex_;
+  std::unique_ptr<impl> pimpl_;
+  std::string config_file_path_;
+  std::string config_backup_path_;
+  std::chrono::milliseconds config_save_delay_;
+  size_t temp_devices_capacity_;
+  bool is_restricted_mode_;
+  bool is_single_user_mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(StorageModule);
+};
+
+}  // namespace storage
+}  // namespace bluetooth
diff --git a/gd/storage/storage_module_test.cc b/gd/storage/storage_module_test.cc
new file mode 100644
index 0000000..d1c745b
--- /dev/null
+++ b/gd/storage/storage_module_test.cc
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ */
+
+#include "storage/storage_module.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <cstdio>
+#include <ctime>
+#include <filesystem>
+#include <iomanip>
+#include <optional>
+#include <thread>
+
+#include "module.h"
+#include "os/files.h"
+#include "storage/config_cache.h"
+#include "storage/legacy_config_file.h"
+
+namespace testing {
+
+using bluetooth::TestModuleRegistry;
+using bluetooth::storage::ConfigCache;
+using bluetooth::storage::LegacyConfigFile;
+using bluetooth::storage::StorageModule;
+
+static const std::chrono::milliseconds kTestConfigSaveDelay = std::chrono::milliseconds(100);
+// Assume it takes at most 1 second to write the file
+static const std::chrono::milliseconds kTestConfigSaveWaitDelay =
+    kTestConfigSaveDelay + std::chrono::milliseconds(1000);
+
+static std::optional<std::chrono::system_clock::time_point> ParseTimestamp(
+    const std::string& timestamp, const std::string& format) {
+  std::istringstream ss(timestamp);
+  // 1. Parse to time_t from timestamp that may not contain day light saving information
+  std::tm no_dst_tm = {};
+  ss >> std::get_time(&no_dst_tm, format.c_str());
+  if (ss.fail()) {
+    return std::nullopt;
+  }
+  // 2. Make a copy of the parsed result so that we can set tm_isdst bit later
+  auto dst_tm = no_dst_tm;
+  auto no_dst_time_t = std::mktime(&no_dst_tm);
+  if (no_dst_time_t == -1) {
+    return std::nullopt;
+  }
+  // 3. Convert time_t to tm again, but let system decide if day light saving should be set at that date and time
+  auto dst_tm_only = std::localtime(&no_dst_time_t);
+  // 4. Set the correct tm_isdst bit
+  dst_tm.tm_isdst = dst_tm_only->tm_isdst;
+  auto dst_time_t = std::mktime(&dst_tm);
+  if (dst_time_t == -1) {
+    return std::nullopt;
+  }
+  // 5. Parse is to time point
+  return std::chrono::system_clock::from_time_t(dst_time_t);
+}
+
+class TestStorageModule : public StorageModule {
+ public:
+  TestStorageModule(
+      std::string config_file_path,
+      std::chrono::milliseconds config_save_delay,
+      size_t temp_devices_capacity,
+      bool is_restricted_mode,
+      bool is_single_user_mode)
+      : StorageModule(
+            std::move(config_file_path),
+            config_save_delay,
+            temp_devices_capacity,
+            is_restricted_mode,
+            is_single_user_mode) {}
+
+  ConfigCache* GetConfigCachePublic() {
+    return StorageModule::GetConfigCache();
+  }
+
+  void SaveImmediatelyPublic() {
+    StorageModule::SaveImmediately();
+  }
+};
+
+class StorageModuleTest : public Test {
+ protected:
+  void SetUp() override {
+    temp_dir_ = std::filesystem::temp_directory_path();
+    temp_config_ = temp_dir_ / "temp_config.txt";
+    temp_backup_config_ = temp_dir_ / "temp_config.bak";
+    DeleteConfigFiles();
+    ASSERT_FALSE(std::filesystem::exists(temp_config_));
+    ASSERT_FALSE(std::filesystem::exists(temp_backup_config_));
+  }
+
+  void TearDown() override {
+    DeleteConfigFiles();
+  }
+
+  void DeleteConfigFiles() {
+    if (std::filesystem::exists(temp_config_)) {
+      EXPECT_TRUE(std::filesystem::remove(temp_config_));
+    }
+    if (std::filesystem::exists(temp_backup_config_)) {
+      EXPECT_TRUE(std::filesystem::remove(temp_backup_config_));
+    }
+  }
+
+  std::filesystem::path temp_dir_;
+  std::filesystem::path temp_config_;
+  std::filesystem::path temp_backup_config_;
+};
+
+TEST_F(StorageModuleTest, empty_config_no_op_test) {
+  // Actual test
+  auto time_before = std::chrono::system_clock::now();
+  auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, 10, false, false);
+  TestModuleRegistry test_registry;
+  test_registry.InjectTestModule(&StorageModule::Factory, storage);
+  test_registry.StopAll();
+  auto time_after = std::chrono::system_clock::now();
+
+  // Verify states after test
+  ASSERT_TRUE(std::filesystem::exists(temp_config_));
+
+  // Verify config after test
+  auto config = LegacyConfigFile::FromPath(temp_config_.string()).Read(10);
+  ASSERT_TRUE(config);
+  EXPECT_TRUE(config->HasSection(StorageModule::kInfoSection));
+  EXPECT_THAT(
+      config->GetProperty(StorageModule::kInfoSection, StorageModule::kFileSourceProperty), Optional(StrEq("Empty")));
+
+  // Verify file creation timestamp falls between time_before and time_after
+  auto timestamp = config->GetProperty(StorageModule::kInfoSection, StorageModule::kTimeCreatedProperty);
+  EXPECT_TRUE(timestamp);
+  auto file_time = ParseTimestamp(*timestamp, StorageModule::kTimeCreatedFormat);
+  EXPECT_TRUE(file_time);
+  EXPECT_GE(std::chrono::duration_cast<std::chrono::seconds>(time_after - *file_time).count(), 0);
+  EXPECT_GE(std::chrono::duration_cast<std::chrono::seconds>(*file_time - time_before).count(), 0);
+}
+
+static const std::string kReadTestConfig =
+    "[Info]\n"
+    "FileSource = Empty\n"
+    "TimeCreated = 2020-05-20 01:20:56\n"
+    "\n"
+    "[Metrics]\n"
+    "Salt256Bit = 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n"
+    "\n"
+    "[Adapter]\n"
+    "Address = 01:02:03:ab:cd:ef\n"
+    "LE_LOCAL_KEY_IRK = fedcba0987654321fedcba0987654321\n"
+    "LE_LOCAL_KEY_IR = fedcba0987654321fedcba0987654322\n"
+    "LE_LOCAL_KEY_DHK = fedcba0987654321fedcba0987654323\n"
+    "LE_LOCAL_KEY_ER = fedcba0987654321fedcba0987654324\n"
+    "ScanMode = 2\n"
+    "DiscoveryTimeout = 120\n"
+    "\n"
+    "[01:02:03:ab:cd:ea]\n"
+    "name = hello world\n"
+    "LinkKey = fedcba0987654321fedcba0987654328\n"
+    "\n";
+
+TEST_F(StorageModuleTest, read_existing_config_test) {
+  ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
+  // Actual test
+
+  // Set up
+  auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, 10, false, false);
+  TestModuleRegistry test_registry;
+  test_registry.InjectTestModule(&StorageModule::Factory, storage);
+
+  // Test
+  ASSERT_NE(storage->GetConfigCachePublic(), nullptr);
+  EXPECT_TRUE(storage->GetConfigCachePublic()->HasSection("Metrics"));
+  EXPECT_THAT(storage->GetConfigCachePublic()->GetPersistentDevices(), ElementsAre("01:02:03:ab:cd:ea"));
+  EXPECT_THAT(
+      storage->GetConfigCachePublic()->GetProperty(StorageModule::kAdapterSection, "Address"),
+      Optional(StrEq("01:02:03:ab:cd:ef")));
+
+  // Tear down
+  test_registry.StopAll();
+
+  // Verify states after test
+  EXPECT_TRUE(std::filesystem::exists(temp_config_));
+
+  // Verify config after test
+  auto config = bluetooth::os::ReadSmallFile(temp_config_.string());
+  EXPECT_TRUE(config);
+  EXPECT_EQ(*config, kReadTestConfig);
+}
+
+TEST_F(StorageModuleTest, save_config_test) {
+  // Prepare config file
+  ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
+
+  // Set up
+  auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, 10, false, false);
+  TestModuleRegistry test_registry;
+  test_registry.InjectTestModule(&StorageModule::Factory, storage);
+
+  // Test
+  ASSERT_NE(storage->GetConfigCachePublic(), nullptr);
+
+  // Change a property
+  EXPECT_THAT(
+      storage->GetConfigCachePublic()->GetProperty("01:02:03:ab:cd:ea", "name"), Optional(StrEq("hello world")));
+  storage->GetConfigCachePublic()->SetProperty("01:02:03:ab:cd:ea", "name", "foo");
+  EXPECT_THAT(storage->GetConfigCachePublic()->GetProperty("01:02:03:ab:cd:ea", "name"), Optional(StrEq("foo")));
+  std::this_thread::sleep_for(kTestConfigSaveWaitDelay);
+  auto config = LegacyConfigFile::FromPath(temp_config_.string()).Read(10);
+  ASSERT_TRUE(config);
+  EXPECT_THAT(config->GetProperty("01:02:03:ab:cd:ea", "name"), Optional(StrEq("foo")));
+
+  // Remove a property
+  storage->GetConfigCachePublic()->RemoveProperty("01:02:03:ab:cd:ea", "name");
+  std::this_thread::sleep_for(kTestConfigSaveWaitDelay);
+  config = LegacyConfigFile::FromPath(temp_config_.string()).Read(10);
+  ASSERT_TRUE(config);
+  EXPECT_FALSE(config->HasProperty("01:02:03:ab:cd:ea", "name"));
+
+  // Remove a section
+  storage->GetConfigCachePublic()->RemoveSection("01:02:03:ab:cd:ea");
+  std::this_thread::sleep_for(kTestConfigSaveWaitDelay);
+  config = LegacyConfigFile::FromPath(temp_config_.string()).Read(10);
+  ASSERT_TRUE(config);
+  EXPECT_FALSE(config->HasSection("01:02:03:ab:cd:ea"));
+
+  // Add a section and save immediately
+  storage->GetConfigCachePublic()->SetProperty("01:02:03:ab:cd:eb", "LinkKey", "123456");
+  storage->SaveImmediatelyPublic();
+  config = LegacyConfigFile::FromPath(temp_config_.string()).Read(10);
+  ASSERT_TRUE(config);
+  EXPECT_TRUE(config->HasSection("01:02:03:ab:cd:eb"));
+
+  // Tear down
+  test_registry.StopAll();
+
+  // Verify states after test
+  EXPECT_TRUE(std::filesystem::exists(temp_config_));
+}
+
+}  // namespace testing
\ No newline at end of file
diff --git a/hci/Android.bp b/hci/Android.bp
index ddfef504..7e4c52a 100644
--- a/hci/Android.bp
+++ b/hci/Android.bp
@@ -35,6 +35,7 @@
         "system/bt/stack/include",
         "system/bt/utils/include",
         "system/bt/bta/include",
+        "system/bt/btif/include",
         "system/libhwbinder/include",
     ],
 }
diff --git a/hci/include/hci_layer.h b/hci/include/hci_layer.h
index bf4efee..401e90a 100644
--- a/hci/include/hci_layer.h
+++ b/hci/include/hci_layer.h
@@ -100,3 +100,4 @@
 void post_to_main_message_loop(const base::Location& from_here, BT_HDR* p_msg);
 
 void hci_layer_cleanup_interface();
+bool hci_is_root_inflammation_event_received();
diff --git a/hci/src/btsnoop_net.cc b/hci/src/btsnoop_net.cc
index a549205..23da481 100644
--- a/hci/src/btsnoop_net.cc
+++ b/hci/src/btsnoop_net.cc
@@ -32,6 +32,7 @@
 
 #include <mutex>
 
+#include "bt_types.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 
@@ -56,8 +57,7 @@
   listen_thread_valid_ =
       (pthread_create(&listen_thread_, NULL, listen_fn_, NULL) == 0);
   if (!listen_thread_valid_)
-    LOG_ERROR(LOG_TAG, "%s pthread_create failed: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s pthread_create failed: %s", __func__, strerror(errno));
 }
 
 void btsnoop_net_close() {
@@ -96,15 +96,13 @@
 
   listen_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   if (listen_socket_ == -1) {
-    LOG_ERROR(LOG_TAG, "%s socket creation failed: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s socket creation failed: %s", __func__, strerror(errno));
     goto cleanup;
   }
 
   if (setsockopt(listen_socket_, SOL_SOCKET, SO_REUSEADDR, &enable,
                  sizeof(enable)) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to set SO_REUSEADDR: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to set SO_REUSEADDR: %s", __func__, strerror(errno));
     goto cleanup;
   }
 
@@ -113,13 +111,12 @@
   addr.sin_addr.s_addr = htonl(LOCALHOST_);
   addr.sin_port = htons(LISTEN_PORT_);
   if (bind(listen_socket_, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to bind listen socket: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to bind listen socket: %s", __func__, strerror(errno));
     goto cleanup;
   }
 
   if (listen(listen_socket_, 10) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to listen: %s", __func__, strerror(errno));
+    LOG_ERROR("%s unable to listen: %s", __func__, strerror(errno));
     goto cleanup;
   }
 
@@ -130,8 +127,7 @@
       if (errno == EINVAL || errno == EBADF) {
         break;
       }
-      LOG_WARN(LOG_TAG, "%s error accepting socket: %s", __func__,
-               strerror(errno));
+      LOG_WARN("%s error accepting socket: %s", __func__, strerror(errno));
       continue;
     }
 
diff --git a/hci/src/hci_inject.cc b/hci/src/hci_inject.cc
index 6d3e8e0..daef27a 100644
--- a/hci/src/hci_inject.cc
+++ b/hci/src/hci_inject.cc
@@ -122,7 +122,7 @@
     case HCI_PACKET_ISO_DATA:
       return MSG_STACK_TO_HC_HCI_ISO;
     default:
-      LOG_ERROR(LOG_TAG, "%s unsupported packet type: %d", __func__, packet);
+      LOG_ERROR("%s unsupported packet type: %d", __func__, packet);
       return -1;
   }
 }
@@ -139,7 +139,7 @@
   client->socket = socket;
 
   if (!list_append(clients, client)) {
-    LOG_ERROR(LOG_TAG, "%s unable to add client to list.", __func__);
+    LOG_ERROR("%s unable to add client to list.", __func__);
     client_free(client);
     return;
   }
@@ -183,7 +183,7 @@
       memcpy(buf->data, buffer + 3, packet_len);
       hci->transmit_downward(buf->event, buf);
     } else {
-      LOG_ERROR(LOG_TAG, "%s dropping injected packet of length %zu", __func__,
+      LOG_ERROR("%s dropping injected packet of length %zu", __func__,
                 packet_len);
     }
 
diff --git a/hci/src/hci_layer.cc b/hci/src/hci_layer.cc
index da6054b..0b96999 100644
--- a/hci/src/hci_layer.cc
+++ b/hci/src/hci_layer.cc
@@ -36,10 +36,12 @@
 #include <mutex>
 
 #include "btcore/include/module.h"
+#include "btif/include/btif_bqr.h"
 #include "btsnoop.h"
 #include "buffer_allocator.h"
 #include "common/message_loop_thread.h"
 #include "common/metrics.h"
+#include "common/once_timer.h"
 #include "hci_inject.h"
 #include "hci_internals.h"
 #include "hcidefs.h"
@@ -55,6 +57,7 @@
 #define BT_HCI_TIMEOUT_TAG_NUM 1010000
 
 using bluetooth::common::MessageLoopThread;
+using bluetooth::common::OnceTimer;
 
 extern void hci_initialize();
 extern void hci_transmit(BT_HDR* packet);
@@ -85,6 +88,7 @@
 static const uint32_t COMMAND_PENDING_TIMEOUT_MS = 2000;
 static const uint32_t COMMAND_PENDING_MUTEX_ACQUIRE_TIMEOUT_MS = 500;
 static const uint32_t COMMAND_TIMEOUT_RESTART_MS = 5000;
+static const uint32_t ROOT_INFLAMMED_RESTART_MS = 5000;
 static const int HCI_UNKNOWN_COMMAND_TIMED_OUT = 0x00ffffff;
 static const int HCI_STARTUP_TIMED_OUT = 0x00eeeeee;
 
@@ -111,7 +115,11 @@
 static alarm_t* command_response_timer;
 static list_t* commands_pending_response;
 static std::recursive_timed_mutex commands_pending_response_mutex;
-static alarm_t* hci_timeout_abort_timer;
+static OnceTimer abort_timer;
+
+// Root inflammation error codes
+static uint8_t root_inflamed_error_code = 0;
+static uint8_t root_inflamed_vendor_error_code = 0;
 
 // The hand-off point for data going to a higher layer, set by the higher layer
 static base::Callback<void(const base::Location&, BT_HDR*)> send_data_upwards;
@@ -120,6 +128,8 @@
 static waiting_command_t* get_waiting_command(command_opcode_t opcode);
 static int get_num_waiting_commands();
 
+static void hci_root_inflamed_abort();
+static void hci_timeout_abort(void);
 static void event_finish_startup(void* context);
 static void startup_timer_expired(void* context);
 
@@ -135,6 +145,8 @@
 static void dispatch_reassembled(BT_HDR* packet);
 static void fragmenter_transmit_finished(BT_HDR* packet,
                                          bool all_fragments_sent);
+static bool filter_bqr_event(int16_t bqr_parameter_length,
+                             uint8_t* p_bqr_event);
 
 static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks = {
     transmit_fragment, dispatch_reassembled, fragmenter_transmit_finished};
@@ -166,12 +178,24 @@
   packet_fragmenter->reassemble_and_dispatch(packet);
 }
 
+void hal_service_died() {
+  if (abort_timer.IsScheduled()) {
+    if (root_inflamed_vendor_error_code != 0 || root_inflamed_error_code != 0) {
+      hci_root_inflamed_abort();
+    } else {
+      hci_timeout_abort();
+    }
+    return;
+  }
+  abort();
+}
+
 // Module lifecycle functions
 
 static future_t* hci_module_shut_down();
 
 static future_t* hci_module_start_up(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
 
   // The host is only allowed to send at most one command initially,
   // as per the Bluetooth spec, Volume 2, Part E, 4.4 (Command Flow Control)
@@ -191,30 +215,29 @@
 
   startup_timer = alarm_new("hci.startup_timer");
   if (!startup_timer) {
-    LOG_ERROR(LOG_TAG, "%s unable to create startup timer.", __func__);
+    LOG_ERROR("%s unable to create startup timer.", __func__);
     goto error;
   }
 
   command_response_timer = alarm_new("hci.command_response_timer");
   if (!command_response_timer) {
-    LOG_ERROR(LOG_TAG, "%s unable to create command response timer.", __func__);
+    LOG_ERROR("%s unable to create command response timer.", __func__);
     goto error;
   }
 
   hci_thread.StartUp();
   if (!hci_thread.IsRunning()) {
-    LOG_ERROR(LOG_TAG, "%s unable to start thread.", __func__);
+    LOG_ERROR("%s unable to start thread.", __func__);
     goto error;
   }
   if (!hci_thread.EnableRealTimeScheduling()) {
-    LOG_ERROR(LOG_TAG, "%s unable to make thread RT.", __func__);
+    LOG_ERROR("%s unable to make thread RT.", __func__);
     goto error;
   }
 
   commands_pending_response = list_new(NULL);
   if (!commands_pending_response) {
-    LOG_ERROR(LOG_TAG,
-              "%s unable to create list for commands pending response.",
+    LOG_ERROR("%s unable to create list for commands pending response.",
               __func__);
     goto error;
   }
@@ -229,7 +252,7 @@
 
   hci_thread.DoInThread(FROM_HERE, base::Bind(&hci_initialize));
 
-  LOG_DEBUG(LOG_TAG, "%s starting async portion", __func__);
+  LOG_DEBUG("%s starting async portion", __func__);
   return local_startup_future;
 
 error:
@@ -238,7 +261,7 @@
 }
 
 static future_t* hci_module_shut_down() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
 
   // Free the timers
   {
@@ -264,12 +287,6 @@
 
   packet_fragmenter->cleanup();
 
-  // Clean up abort timer, if it exists.
-  if (hci_timeout_abort_timer != NULL) {
-    alarm_free(hci_timeout_abort_timer);
-    hci_timeout_abort_timer = NULL;
-  }
-
   if (hci_firmware_log_fd != INVALID_FD) {
     hci_close_firmware_log_file(hci_firmware_log_fd);
     hci_firmware_log_fd = INVALID_FD;
@@ -335,8 +352,7 @@
   if (type == MSG_STACK_TO_HC_HCI_CMD) {
     // TODO(zachoverflow): eliminate this call
     transmit_command((BT_HDR*)data, NULL, NULL, NULL);
-    LOG_WARN(LOG_TAG,
-             "%s legacy transmit of command. Use transmit_command instead.",
+    LOG_WARN("%s legacy transmit of command. Use transmit_command instead.",
              __func__);
   } else {
     enqueue_packet(data);
@@ -346,7 +362,7 @@
 // Start up functions
 
 static void event_finish_startup(UNUSED_ATTR void* context) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
   std::lock_guard<std::recursive_timed_mutex> lock(
       commands_pending_response_mutex);
   alarm_cancel(startup_timer);
@@ -358,9 +374,16 @@
 }
 
 static void startup_timer_expired(UNUSED_ATTR void* context) {
-  LOG_ERROR(LOG_TAG, "%s", __func__);
+  LOG_ERROR("%s", __func__);
 
   LOG_EVENT_INT(BT_HCI_TIMEOUT_TAG_NUM, HCI_STARTUP_TIMED_OUT);
+
+  hci_close();
+  if (abort_timer.IsScheduled()) {
+    LOG_ERROR("%s: waiting for abort_timer", __func__);
+    return;
+  }
+
   abort();
 }
 
@@ -442,8 +465,8 @@
 }
 
 // Abort.  The chip has had time to write any debugging information.
-static void hci_timeout_abort(void* unused_data) {
-  LOG_ERROR(LOG_TAG, "%s restarting the Bluetooth process.", __func__);
+static void hci_timeout_abort(void) {
+  LOG_ERROR("%s restarting the Bluetooth process.", __func__);
   hci_close_firmware_log_file(hci_firmware_log_fd);
 
   // We shouldn't try to recover the stack from this command timeout.
@@ -451,8 +474,15 @@
   abort();
 }
 
+static void hci_root_inflamed_abort() {
+  LOG(FATAL) << __func__
+             << ": error_code = " << std::to_string(root_inflamed_error_code)
+             << ", vendor_error_code = "
+             << std::to_string(root_inflamed_vendor_error_code);
+}
+
 static void command_timed_out_log_info(void* original_wait_entry) {
-  LOG_ERROR(LOG_TAG, "%s: %d commands pending response", __func__,
+  LOG_ERROR("%s: %d commands pending response", __func__,
             get_num_waiting_commands());
 
   for (const list_node_t* node = list_begin(commands_pending_response);
@@ -464,18 +494,18 @@
         std::chrono::duration_cast<std::chrono::milliseconds>(
             std::chrono::steady_clock::now() - wait_entry->timestamp)
             .count();
-    LOG_ERROR(LOG_TAG, "%s: Waited %d ms for a response to opcode: 0x%x %s",
-              __func__, wait_time_ms, wait_entry->opcode,
+    LOG_ERROR("%s: Waited %d ms for a response to opcode: 0x%x %s", __func__,
+              wait_time_ms, wait_entry->opcode,
               (wait_entry == original_wait_entry) ? "*matches timer*" : "");
 
     // Dump the length field and the first byte of the payload, if present.
     uint8_t* command = wait_entry->command->data + wait_entry->command->offset;
     if (wait_entry->command->len > 3) {
-      LOG_ERROR(LOG_TAG, "%s: Size %d Hex %02x %02x %02x %02x", __func__,
+      LOG_ERROR("%s: Size %d Hex %02x %02x %02x %02x", __func__,
                 wait_entry->command->len, command[0], command[1], command[2],
                 command[3]);
     } else {
-      LOG_ERROR(LOG_TAG, "%s: Size %d Hex %02x %02x %02x", __func__,
+      LOG_ERROR("%s: Size %d Hex %02x %02x %02x", __func__,
                 wait_entry->command->len, command[0], command[1], command[2]);
     }
 
@@ -486,12 +516,12 @@
 
 // Print debugging information and quit. Don't dereference original_wait_entry.
 static void command_timed_out(void* original_wait_entry) {
-  LOG_ERROR(LOG_TAG, "%s", __func__);
+  LOG_ERROR("%s", __func__);
   std::unique_lock<std::recursive_timed_mutex> lock(
       commands_pending_response_mutex, std::defer_lock);
   if (!lock.try_lock_for(std::chrono::milliseconds(
           COMMAND_PENDING_MUTEX_ACQUIRE_TIMEOUT_MS))) {
-    LOG_ERROR(LOG_TAG, "%s: Cannot obtain the mutex", __func__);
+    LOG_ERROR("%s: Cannot obtain the mutex", __func__);
     LOG_EVENT_INT(BT_HCI_TIMEOUT_TAG_NUM, HCI_UNKNOWN_COMMAND_TIMED_OUT);
     bluetooth::common::LogHciTimeoutEvent(android::bluetooth::hci::CMD_UNKNOWN);
   } else {
@@ -500,11 +530,11 @@
   }
 
   // Don't request a firmware dump for multiple hci timeouts
-  if (hci_timeout_abort_timer != NULL || hci_firmware_log_fd != INVALID_FD) {
+  if (hci_firmware_log_fd != INVALID_FD) {
     return;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: requesting a firmware dump.", __func__);
+  LOG_ERROR("%s: requesting a firmware dump.", __func__);
 
   /* Allocate a buffer to hold the HCI command. */
   BT_HDR* bt_hdr =
@@ -524,15 +554,15 @@
   transmit_fragment(bt_hdr, true);
 
   osi_free(bt_hdr);
-  LOG_ERROR(LOG_TAG, "%s: Setting a timer to restart.", __func__);
+  LOG_ERROR("%s: Setting a timer to restart.", __func__);
 
-  hci_timeout_abort_timer = alarm_new("hci.hci_timeout_aborter");
-  if (!hci_timeout_abort_timer) {
-    LOG_ERROR(LOG_TAG, "%s unable to create an abort timer.", __func__);
+  // alarm_default_callbacks thread post to hci_thread.
+  if (!abort_timer.Schedule(
+          hci_thread.GetWeakPtr(), FROM_HERE, base::Bind(hci_timeout_abort),
+          base::TimeDelta::FromMilliseconds(COMMAND_TIMEOUT_RESTART_MS))) {
+    LOG_ERROR("%s unable to create an abort timer.", __func__);
     abort();
   }
-  alarm_set(hci_timeout_abort_timer, COMMAND_TIMEOUT_RESTART_MS,
-            hci_timeout_abort, nullptr);
 }
 
 // Event/packet receiving functions
@@ -556,6 +586,49 @@
   }
 }
 
+bool hci_is_root_inflammation_event_received() {
+  return abort_timer.IsScheduled();
+}
+
+void handle_root_inflammation_event() {
+  LOG(ERROR) << __func__
+             << ": Root inflammation event! setting timer to restart.";
+  // TODO(ugoyu) Report to bluetooth metrics here
+  {
+    // Try to stop hci command and startup timers
+    std::unique_lock<std::recursive_timed_mutex> lock(
+        commands_pending_response_mutex, std::defer_lock);
+    if (lock.try_lock_for(std::chrono::milliseconds(
+            COMMAND_PENDING_MUTEX_ACQUIRE_TIMEOUT_MS))) {
+      if (alarm_is_scheduled(startup_timer)) {
+        alarm_cancel(startup_timer);
+      }
+      if (alarm_is_scheduled(command_response_timer)) {
+        alarm_cancel(command_response_timer);
+      }
+      // Cleanup the hci/startup timers so they will not be scheduled again and
+      // expire before the abort_timer.
+      alarm_free(command_response_timer);
+      command_response_timer = NULL;
+      alarm_free(startup_timer);
+      startup_timer = NULL;
+    } else {
+      LOG(ERROR) << __func__ << ": Failed to obtain mutex";
+      hci_root_inflamed_abort();
+    }
+  }
+
+  // HwBinder thread post to hci_thread
+  if (!hci_thread.IsRunning() ||
+      !abort_timer.Schedule(
+          hci_thread.GetWeakPtr(), FROM_HERE,
+          base::Bind(hci_root_inflamed_abort),
+          base::TimeDelta::FromMilliseconds(ROOT_INFLAMMED_RESTART_MS))) {
+    LOG(ERROR) << "Failed to schedule abort_timer or hci has already closed!";
+    hci_root_inflamed_abort();
+  }
+}
+
 // Returns true if the event was intercepted and should not proceed to
 // higher layers. Also inspects an incoming event for interesting
 // information, like how many commands are now able to be sent.
@@ -579,10 +652,10 @@
 
     if (!wait_entry) {
       if (opcode != HCI_COMMAND_NONE) {
-        LOG_WARN(LOG_TAG,
-                 "%s command complete event with no matching command (opcode: "
-                 "0x%04x).",
-                 __func__, opcode);
+        LOG_WARN(
+            "%s command complete event with no matching command (opcode: "
+            "0x%04x).",
+            __func__, opcode);
       }
     } else {
       update_command_response_timer();
@@ -608,7 +681,6 @@
 
     if (!wait_entry) {
       LOG_WARN(
-          LOG_TAG,
           "%s command status event with no matching command. opcode: 0x%04x",
           __func__, opcode);
     } else {
@@ -619,15 +691,28 @@
     }
 
     goto intercepted;
-  } else if (event_code == HCI_VSE_SUBCODE_DEBUG_INFO_SUB_EVT) {
-    if (hci_firmware_log_fd == INVALID_FD)
-      hci_firmware_log_fd = hci_open_firmware_log_file();
+  } else if (event_code == HCI_VENDOR_SPECIFIC_EVT) {
+    uint8_t sub_event_code;
+    STREAM_TO_UINT8(sub_event_code, stream);
 
-    if (hci_firmware_log_fd != INVALID_FD)
-      hci_log_firmware_debug_packet(hci_firmware_log_fd, packet);
+    if (sub_event_code == HCI_VSE_SUBCODE_DEBUG_INFO_SUB_EVT) {
+      if (hci_firmware_log_fd == INVALID_FD)
+        hci_firmware_log_fd = hci_open_firmware_log_file();
 
-    buffer_allocator->free(packet);
-    return true;
+      if (hci_firmware_log_fd != INVALID_FD)
+        hci_log_firmware_debug_packet(hci_firmware_log_fd, packet);
+
+      buffer_allocator->free(packet);
+      return true;
+    } else if (sub_event_code == HCI_VSE_SUBCODE_BQR_SUB_EVT) {
+      // Excluding the HCI Event packet header and 1 octet sub-event code
+      int16_t bqr_parameter_length = packet->len - HCIE_PREAMBLE_SIZE - 1;
+      // The stream currently points to the BQR sub-event parameters
+      if (filter_bqr_event(bqr_parameter_length, stream)) {
+        buffer_allocator->free(packet);
+        return true;
+      }
+    }
   }
 
   return false;
@@ -700,6 +785,60 @@
   }
 }
 
+// Returns true if the BQR event is handled and should not proceed to
+// higher layers.
+static bool filter_bqr_event(int16_t bqr_parameter_length,
+                             uint8_t* p_bqr_event) {
+  if (bqr_parameter_length <= 0) {
+    LOG(ERROR) << __func__ << ": Invalid parameter length : "
+               << std::to_string(bqr_parameter_length);
+    return true;
+  }
+
+  bool intercepted = false;
+  uint8_t quality_report_id = p_bqr_event[0];
+  switch (quality_report_id) {
+    case bluetooth::bqr::QUALITY_REPORT_ID_ROOT_INFLAMMATION:
+      if (bqr_parameter_length >=
+          bluetooth::bqr::kRootInflammationParamTotalLen) {
+        STREAM_TO_UINT8(quality_report_id, p_bqr_event);
+        STREAM_TO_UINT8(root_inflamed_error_code, p_bqr_event);
+        STREAM_TO_UINT8(root_inflamed_vendor_error_code, p_bqr_event);
+        handle_root_inflammation_event();
+      }
+      intercepted = true;
+      break;
+
+    case bluetooth::bqr::QUALITY_REPORT_ID_LMP_LL_MESSAGE_TRACE:
+      if (bqr_parameter_length >= bluetooth::bqr::kLogDumpParamTotalLen) {
+        bluetooth::bqr::DumpLmpLlMessage(bqr_parameter_length, p_bqr_event);
+      }
+      intercepted = true;
+      break;
+
+    case bluetooth::bqr::QUALITY_REPORT_ID_BT_SCHEDULING_TRACE:
+      if (bqr_parameter_length >= bluetooth::bqr::kLogDumpParamTotalLen) {
+        bluetooth::bqr::DumpBtScheduling(bqr_parameter_length, p_bqr_event);
+      }
+      intercepted = true;
+      break;
+
+    case bluetooth::bqr::QUALITY_REPORT_ID_CONTROLLER_DBG_INFO:
+      // TODO: Integrate with the HCI_VSE_SUBCODE_DEBUG_INFO_SUB_EVT
+      intercepted = true;
+      break;
+
+    case bluetooth::bqr::QUALITY_REPORT_ID_MONITOR_MODE:
+    case bluetooth::bqr::QUALITY_REPORT_ID_APPROACH_LSTO:
+    case bluetooth::bqr::QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY:
+    case bluetooth::bqr::QUALITY_REPORT_ID_SCO_VOICE_CHOPPY:
+    default:
+      break;
+  }
+
+  return intercepted;
+}
+
 static void init_layer_interface() {
   if (!interface_created) {
     // It's probably ok for this to live forever. It's small and
diff --git a/hci/src/hci_layer_android.cc b/hci/src/hci_layer_android.cc
index c4aded1..ddbc255 100644
--- a/hci/src/hci_layer_android.cc
+++ b/hci/src/hci_layer_android.cc
@@ -54,6 +54,8 @@
 extern void acl_event_received(BT_HDR* packet);
 extern void sco_data_received(BT_HDR* packet);
 extern void iso_data_received(BT_HDR* packet);
+extern void hal_service_died();
+extern bool hci_is_root_inflammation_event_received();
 
 android::sp<V1_0::IBluetoothHci> btHci;
 android::sp<V1_1::IBluetoothHci> btHci_1_1;
@@ -61,8 +63,8 @@
 class BluetoothHciDeathRecipient : public hidl_death_recipient {
  public:
   virtual void serviceDied(uint64_t /*cookie*/, const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
-    LOG_ERROR(LOG_TAG, "Bluetooth HAL service died!");
-    abort();
+    LOG_ERROR("Bluetooth HAL service died!");
+    hal_service_died();
   }
 };
 android::sp<BluetoothHciDeathRecipient> bluetoothHciDeathRecipient = new BluetoothHciDeathRecipient();
@@ -88,6 +90,14 @@
   }
 
   Return<void> initializationComplete(Status status) override {
+    if (hci_is_root_inflammation_event_received()) {
+      // Ignore the initializationComplete here as we have already received
+      // root inflammation event earlier.
+      LOG_ERROR(
+          "initializationComplete after root inflammation event! status=%d",
+          status);
+      return Void();
+    }
     CHECK(status == Status::SUCCESS);
     initialization_complete();
     return Void();
@@ -121,7 +131,7 @@
 };
 
 void hci_initialize() {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO("%s", __func__);
 
   btHci_1_1 = V1_1::IBluetoothHci::getService();
 
@@ -135,18 +145,19 @@
   CHECK(btHci != nullptr);
   auto death_link = btHci->linkToDeath(bluetoothHciDeathRecipient, 0);
   if (!death_link.isOk()) {
-    LOG_ERROR(LOG_TAG, "%s: Unable to set the death recipient for the Bluetooth HAL", __func__);
+    LOG_ERROR("%s: Unable to set the death recipient for the Bluetooth HAL",
+              __func__);
     abort();
   }
-  LOG_INFO(LOG_TAG, "%s: IBluetoothHci::getService() returned %p (%s)",
-           __func__, btHci.get(), (btHci->isRemote() ? "remote" : "local"));
+  LOG_INFO("%s: IBluetoothHci::getService() returned %p (%s)", __func__,
+           btHci.get(), (btHci->isRemote() ? "remote" : "local"));
 
   // Block allows allocation of a variable that might be bypassed by goto.
   {
     android::sp<V1_1::IBluetoothHciCallbacks> callbacks =
         new BluetoothHciCallbacks();
     if (btHci_1_1 != nullptr) {
-      btHci_1_1->initialize(callbacks);
+      btHci_1_1->initialize_1_1(callbacks);
     } else {
       btHci->initialize(callbacks);
     }
@@ -157,11 +168,15 @@
   if (btHci != nullptr) {
     auto death_unlink = btHci->unlinkToDeath(bluetoothHciDeathRecipient);
     if (!death_unlink.isOk()) {
-      LOG_ERROR(LOG_TAG, "%s: Error unlinking death recipient from the Bluetooth HAL", __func__);
+      LOG_ERROR("%s: Error unlinking death recipient from the Bluetooth HAL",
+                __func__);
     }
+    auto close_status = btHci->close();
+    if (!close_status.isOk()) {
+      LOG_ERROR("%s: Error closing the Bluetooth HAL", __func__);
+    }
+    btHci = nullptr;
   }
-  btHci->close();
-  btHci = nullptr;
 }
 
 void hci_transmit(BT_HDR* packet) {
@@ -183,19 +198,19 @@
       if (btHci_1_1 != nullptr) {
         btHci_1_1->sendIsoData(data);
       } else {
-        LOG_ERROR(LOG_TAG, "ISO is not supported in HAL v1.0");
+        LOG_ERROR("ISO is not supported in HAL v1.0");
       }
       break;
     default:
-      LOG_ERROR(LOG_TAG, "Unknown packet type (%d)", event);
+      LOG_ERROR("Unknown packet type (%d)", event);
       break;
   }
 }
 
 int hci_open_firmware_log_file() {
   if (rename(LOG_PATH, LAST_LOG_PATH) == -1 && errno != ENOENT) {
-    LOG_ERROR(LOG_TAG, "%s unable to rename '%s' to '%s': %s", __func__,
-              LOG_PATH, LAST_LOG_PATH, strerror(errno));
+    LOG_ERROR("%s unable to rename '%s' to '%s': %s", __func__, LOG_PATH,
+              LAST_LOG_PATH, strerror(errno));
   }
 
   mode_t prevmask = umask(0);
@@ -203,7 +218,7 @@
                         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
   umask(prevmask);
   if (logfile_fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s unable to open '%s': %s", __func__, LOG_PATH,
+    LOG_ERROR("%s unable to open '%s': %s", __func__, LOG_PATH,
               strerror(errno));
   }
 
diff --git a/hci/src/hci_packet_parser.cc b/hci/src/hci_packet_parser.cc
index b1efd44..d799a0a 100644
--- a/hci/src/hci_packet_parser.cc
+++ b/hci/src/hci_packet_parser.cc
@@ -258,7 +258,7 @@
   STREAM_TO_UINT8(status, stream);
 
   if (status != HCI_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: return status - 0x%x", __func__, status);
+    LOG_ERROR("%s: return status - 0x%x", __func__, status);
     return NULL;
   }
 
diff --git a/hci/src/packet_fragmenter.cc b/hci/src/packet_fragmenter.cc
index f688982..c385014 100644
--- a/hci/src/packet_fragmenter.cc
+++ b/hci/src/packet_fragmenter.cc
@@ -135,7 +135,7 @@
 
     if (boundary_flag == START_PACKET_BOUNDARY) {
       if (acl_length < 2) {
-        LOG_WARN(LOG_TAG, "%s invalid acl_length %d", __func__, acl_length);
+        LOG_WARN("%s invalid acl_length %d", __func__, acl_length);
         buffer_allocator->free(packet);
         return;
       }
@@ -143,10 +143,10 @@
       STREAM_TO_UINT16(l2cap_length, stream);
       auto map_iter = partial_packets.find(handle);
       if (map_iter != partial_packets.end()) {
-        LOG_WARN(LOG_TAG,
-                 "%s found unfinished packet for handle with start packet. "
-                 "Dropping old.",
-                 __func__);
+        LOG_WARN(
+            "%s found unfinished packet for handle with start packet. "
+            "Dropping old.",
+            __func__);
 
         BT_HDR* hdl = map_iter->second;
         partial_packets.erase(map_iter);
@@ -154,8 +154,8 @@
       }
 
       if (acl_length < L2CAP_HEADER_PDU_LEN_SIZE) {
-        LOG_WARN(LOG_TAG, "%s L2CAP packet too small (%d < %d). Dropping it.",
-                 __func__, packet->len, L2CAP_HEADER_PDU_LEN_SIZE);
+        LOG_WARN("%s L2CAP packet too small (%d < %d). Dropping it.", __func__,
+                 packet->len, L2CAP_HEADER_PDU_LEN_SIZE);
         buffer_allocator->free(packet);
         return;
       }
@@ -168,7 +168,7 @@
       if (check_uint16_overflow(l2cap_length,
                                 (L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE)) ||
           ((full_length + sizeof(BT_HDR)) > BT_DEFAULT_BUFFER_SIZE)) {
-        LOG_ERROR(LOG_TAG, "%s Dropping L2CAP packet with invalid length (%d).",
+        LOG_ERROR("%s Dropping L2CAP packet with invalid length (%d).",
                   __func__, l2cap_length);
         buffer_allocator->free(packet);
         return;
@@ -176,8 +176,7 @@
 
       if (full_length <= packet->len) {
         if (full_length < packet->len)
-          LOG_WARN(LOG_TAG,
-                   "%s found l2cap full length %d less than the hci length %d.",
+          LOG_WARN("%s found l2cap full length %d less than the hci length %d.",
                    __func__, l2cap_length, packet->len);
 
         callbacks->reassembled(packet);
@@ -204,8 +203,7 @@
     } else {
       auto map_iter = partial_packets.find(handle);
       if (map_iter == partial_packets.end()) {
-        LOG_WARN(LOG_TAG,
-                 "%s got continuation for unknown packet. Dropping it.",
+        LOG_WARN("%s got continuation for unknown packet. Dropping it.",
                  __func__);
         buffer_allocator->free(packet);
         return;
@@ -217,10 +215,10 @@
           partial_packet->offset + (packet->len - HCI_ACL_PREAMBLE_SIZE);
       if (projected_offset >
           partial_packet->len) {  // len stores the expected length
-        LOG_WARN(LOG_TAG,
-                 "%s got packet which would exceed expected length of %d. "
-                 "Truncating.",
-                 __func__, partial_packet->len);
+        LOG_WARN(
+            "%s got packet which would exceed expected length of %d. "
+            "Truncating.",
+            __func__, partial_packet->len);
         packet->len = (partial_packet->len - partial_packet->offset) + packet->offset;
         projected_offset = partial_packet->len;
       }
diff --git a/include/hardware/bluetooth.h b/include/hardware/bluetooth.h
index 063abf9..8468511 100644
--- a/include/hardware/bluetooth.h
+++ b/include/hardware/bluetooth.h
@@ -48,6 +48,7 @@
 #define BT_PROFILE_AV_RC_ID "avrcp"
 #define BT_PROFILE_AV_RC_CTRL_ID "avrcp_ctrl"
 #define BT_PROFILE_HEARING_AID_ID "hearing_aid"
+#define BT_KEYSTORE_ID "bluetooth_keystore"
 
 /** Bluetooth Device Name */
 typedef struct { uint8_t name[249]; } __attribute__((packed)) bt_bdname_t;
@@ -469,10 +470,13 @@
    * The |start_restricted| flag inits the adapter in restricted mode. In
    * restricted mode, bonds that are created are marked as restricted in the
    * config file. These devices are deleted upon leaving restricted mode.
-   * The |is_single_user_mode| flag inits the adapter in NIAP mode.
+   * The |is_niap_mode| flag inits the adapter in NIAP mode.
+   * The |config_compare_result| flag show the config checksum check result if
+   * is in NIAP mode.
+   * The |init_flags| are config flags that cannot change during run.
    */
-  int (*init)(bt_callbacks_t* callbacks, bool guest_mode,
-              bool is_single_user_mode);
+  int (*init)(bt_callbacks_t* callbacks, bool guest_mode, bool is_niap_mode,
+              int config_compare_result, const char** init_flags);
 
   /** Enable Bluetooth. */
   int (*enable)();
@@ -625,6 +629,14 @@
    * @return a string of uint8_t that is unique to this MAC address
    */
   std::string (*obfuscate_address)(const RawAddress& address);
+
+  /**
+   * Get an incremental id for as primary key for Bluetooth metric and log
+   *
+   * @param address Bluetooth MAC address of Bluetooth device
+   * @return int incremental Bluetooth id
+   */
+  int (*get_metric_id)(const RawAddress& address);
 } bt_interface_t;
 
 #define BLUETOOTH_INTERFACE_STRING "bluetoothInterface"
diff --git a/include/hardware/bt_av.h b/include/hardware/bt_av.h
index d38beaa..c4c0ecc 100644
--- a/include/hardware/bt_av.h
+++ b/include/hardware/bt_av.h
@@ -266,6 +266,13 @@
                                                 uint32_t sample_rate,
                                                 uint8_t channel_count);
 
+/** Callback for querying whether the mandatory codec is more preferred.
+ *  Used only for the A2DP Source interface.
+ *  Return true if optional codecs are not preferred.
+ */
+typedef bool (*btav_mandatory_codec_preferred_callback)(
+    const RawAddress& bd_addr);
+
 /** BT-AV A2DP Source callback structure. */
 typedef struct {
   /** set to sizeof(btav_source_callbacks_t) */
@@ -273,6 +280,7 @@
   btav_connection_state_callback connection_state_cb;
   btav_audio_state_callback audio_state_cb;
   btav_audio_source_config_callback audio_config_cb;
+  btav_mandatory_codec_preferred_callback mandatory_codec_preferred_cb;
 } btav_source_callbacks_t;
 
 /** BT-AV A2DP Sink callback structure. */
@@ -303,9 +311,10 @@
   /**
    * Register the BtAv callbacks.
    */
-  bt_status_t (*init)(btav_source_callbacks_t* callbacks,
-                      int max_connected_audio_devices,
-                      std::vector<btav_a2dp_codec_config_t> codec_priorities);
+  bt_status_t (*init)(
+      btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
+      const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+      const std::vector<btav_a2dp_codec_config_t>& offloading_preference);
 
   /** connect to headset */
   bt_status_t (*connect)(const RawAddress& bd_addr);
@@ -353,6 +362,9 @@
 
   /** Sets the audio track gain. */
   void (*set_audio_track_gain)(float gain);
+
+  /** sets the connected device as active */
+  bt_status_t (*set_active_device)(const RawAddress& bd_addr);
 } btav_sink_interface_t;
 
 __END_DECLS
diff --git a/include/hardware/bt_keystore.h b/include/hardware/bt_keystore.h
new file mode 100644
index 0000000..5442c98
--- /dev/null
+++ b/include/hardware/bt_keystore.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+namespace bluetooth {
+namespace bluetooth_keystore {
+
+class BluetoothKeystoreCallbacks {
+ public:
+  virtual ~BluetoothKeystoreCallbacks() = default;
+
+  /** Callback for key encrypt or remove key */
+  virtual void set_encrypt_key_or_remove_key(std::string prefix,
+                                             std::string encryptedString) = 0;
+
+  /** Callback for get key. */
+  virtual std::string get_key(std::string prefix) = 0;
+};
+
+class BluetoothKeystoreInterface {
+ public:
+  virtual ~BluetoothKeystoreInterface() = default;
+
+  /** Register the bluetooth keystore callbacks */
+  virtual void init(BluetoothKeystoreCallbacks* callbacks) = 0;
+
+  /** Interface for key encrypt or remove key */
+  virtual void set_encrypt_key_or_remove_key(std::string prefix,
+                                             std::string encryptedString) = 0;
+
+  /** Interface for get key. */
+  virtual std::string get_key(std::string prefix) = 0;
+
+  /** Interface for clear map. */
+  virtual void clear_map() = 0;
+};
+
+}  // namespace bluetooth_keystore
+}  // namespace bluetooth
diff --git a/include/hardware/bt_rc.h b/include/hardware/bt_rc.h
old mode 100644
new mode 100755
index dc46eca..a4e349d
--- a/include/hardware/bt_rc.h
+++ b/include/hardware/bt_rc.h
@@ -54,6 +54,7 @@
 #define BTRC_MEDIA_ATTR_ID_NUM_TRACKS 0x00000005
 #define BTRC_MEDIA_ATTR_ID_GENRE 0x00000006
 #define BTRC_MEDIA_ATTR_ID_PLAYING_TIME 0x00000007 /* in miliseconds */
+#define BTRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE 0x00000008
 
 /* Macros for folder types */
 #define BTRC_FOLDER_TYPE_MIXED 0x00
@@ -86,6 +87,7 @@
   BTRC_FEAT_METADATA = 0x01,        /* AVRCP 1.3 */
   BTRC_FEAT_ABSOLUTE_VOLUME = 0x02, /* Supports TG role and volume sync */
   BTRC_FEAT_BROWSE = 0x04, /* AVRCP 1.4 and up, with Browsing support */
+  BTRC_FEAT_COVER_ARTWORK = 0x8,    /* AVRCP 1.6 and up, Cover Art */
 } btrc_remote_features_t;
 
 typedef enum {
@@ -617,6 +619,11 @@
     const RawAddress& bd_addr, uint16_t id);
 typedef void (*btrc_ctrl_now_playing_contents_changed_callback)(
     const RawAddress& bd_addr);
+typedef void (*btrc_ctrl_available_player_changed_callback)(
+    const RawAddress& bd_addr);
+
+typedef void (*btrc_ctrl_get_cover_art_psm_callback)(const RawAddress& bd_addr,
+    const uint16_t psm);
 
 /** BT-RC Controller callback structure. */
 typedef struct {
@@ -643,6 +650,8 @@
   btrc_ctrl_addressed_player_changed_callback addressed_player_changed_cb;
   btrc_ctrl_now_playing_contents_changed_callback
       now_playing_contents_changed_cb;
+  btrc_ctrl_available_player_changed_callback available_player_changed_cb;
+  btrc_ctrl_get_cover_art_psm_callback get_cover_art_psm_cb;
 } btrc_ctrl_callbacks_t;
 
 /** Represents the standard BT-RC AVRCP Controller interface. */
@@ -672,6 +681,9 @@
   bt_status_t (*play_item_cmd)(const RawAddress& bd_addr, uint8_t scope,
                                uint8_t* uid, uint16_t uid_counter);
 
+  /** get the current track's media metadata */
+  bt_status_t (*get_current_metadata_cmd)(const RawAddress& bd_addr);
+
   /** get the playback state */
   bt_status_t (*get_playback_state_cmd)(const RawAddress& bd_addr);
 
diff --git a/internal_include/stack_config.h b/internal_include/stack_config.h
index d623fa4..19968ca 100644
--- a/internal_include/stack_config.h
+++ b/internal_include/stack_config.h
@@ -20,7 +20,7 @@
 
 #include <stdbool.h>
 
-#include "module.h"
+#include "btcore/include/module.h"
 #include "osi/include/config.h"
 
 static const char STACK_CONFIG_MODULE[] = "stack_config_module";
diff --git a/main/Android.bp b/main/Android.bp
index 12555f3..b5e44ed 100644
--- a/main/Android.bp
+++ b/main/Android.bp
@@ -8,26 +8,56 @@
         "bte_init_cpp_logging.cc",
         "bte_logmsg.cc",
         "bte_main.cc",
-        "shim/btm.cc",
-        "shim/btm_api.cc",
-        "shim/controller.cc",
-        "shim/entry.cc",
-        "shim/hci_layer.cc",
-        "shim/l2c_api.cc",
-        "shim/l2cap.cc",
-        "shim/shim.cc",
         "stack_config.cc",
     ]
 }
 
+cc_library_static {
+    name: "libbte",
+    defaults: ["fluoride_defaults"],
+    srcs: [
+        ":LibBluetoothSources",
+        ":LibBluetoothShimSources",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/gd",
+        "system/bt/bta/include",
+        "system/bt/bta/sys",
+        "system/bt/bta/dm",
+        "system/bt/btcore/include",
+        "system/bt/internal_include",
+        "system/bt/stack/include",
+        "system/bt/stack/l2cap",
+        "system/bt/stack/a2dp",
+        "system/bt/stack/btm",
+        "system/bt/stack/avdt",
+        "system/bt/udrv/include",
+        "system/bt/btif/include",
+        "system/bt/btif/co",
+        "system/bt/hci/include",
+        "system/bt/vnd/include",
+        "system/bt/embdrv/sbc/encoder/include",
+        "system/bt/embdrv/sbc/decoder/include",
+        "system/bt/utils/include",
+        "system/security/keystore/include",
+        "hardware/interfaces/keymaster/4.0/support/include",
+    ],
+    shared_libs: [
+            "libflatbuffers-cpp",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedDumpsysDataSchema_h",
+        "BluetoothGeneratedPackets_h",
+    ],
+    host_supported: true,
+}
+
 cc_library_shared {
     name: "libbluetooth",
     defaults: ["fluoride_defaults"],
     header_libs: ["libbluetooth_headers"],
     export_header_lib_headers: ["libbluetooth_headers"],
-    srcs: [
-        ":LibBluetoothSources",
-    ],
     include_dirs: [
         "system/bt",
         "system/bt/bta/include",
@@ -60,6 +90,7 @@
         "libaaudio",
         "libcutils",
         "libdl",
+        "libflatbuffers-cpp",
         "libfmq",
         "libhidlbase",
         "liblog",
@@ -69,11 +100,9 @@
         "libtinyxml2",
         "libz",
         "libcrypto",
-        //"android.hardware.keymaster@4.0",
-        //"libkeymaster4support",
-        //"libkeystore_binder",
     ],
     static_libs: [
+        "libbte",
         "libbt-sbc-decoder",
         "libbt-sbc-encoder",
         "libFraunhoferAAC",
@@ -119,11 +148,12 @@
 
     srcs: [
         ":LibBluetoothSources",
-        "shim/entry_for_test.cc",
+        ":LibBluetoothShimSources",
     ],
     host_supported: true,
     include_dirs: [
         "system/bt",
+        "system/bt/gd",
         "system/bt/bta/include",
         "system/bt/btcore/include",
         "system/bt/btif/include",
@@ -132,37 +162,17 @@
         "system/bt/stack/include",
         "system/bt/utils/include",
     ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+        "BluetoothGeneratedDumpsysDataSchema_h",
+    ],
     cflags: [
         "-DBUILDCFG",
     ],
-}
-
-filegroup {
-    name: "BluetoothLegacyShimTestSources",
-    srcs: [
-        "shim/l2cap_test.cc",
-        "shim/test_stack.cc",
-    ]
-}
-
-cc_test {
-    name: "bluetooth_test_legacy",
-    defaults: ["fluoride_defaults",
-               "fluoride_osi_defaults",
-    ],
-    test_suites: ["device-tests"],
-    host_supported: true,
-    srcs: [
-        ":BluetoothLegacyShimTestSources",
-    ],
-    static_libs: [
-        "libgmock",
-        "libbluetooth-for-tests",
-        "libosi",
-    ],
     shared_libs: [
-        "libchrome",
-        "liblog",
+        "libflatbuffers-cpp",
+    ],
+    whole_static_libs: [
+        "libbluetooth_gd", // Gabeldorsche
     ],
 }
-
diff --git a/main/bte_conf.cc b/main/bte_conf.cc
index a60015a..b82969a 100644
--- a/main/bte_conf.cc
+++ b/main/bte_conf.cc
@@ -35,7 +35,7 @@
 
   std::unique_ptr<config_t> config = config_new(p_path);
   if (!config) {
-    LOG_ERROR(LOG_TAG, "%s unable to load DID config '%s'.", __func__, p_path);
+    LOG_ERROR("%s unable to load DID config '%s'.", __func__, p_path);
     return;
   }
 
@@ -44,7 +44,7 @@
     snprintf(section_name, sizeof(section_name), "DID%d", i);
 
     if (!config_has_section(*config, section_name)) {
-      LOG_DEBUG(LOG_TAG, "%s no section named %s.", __func__, section_name);
+      LOG_DEBUG("%s no section named %s.", __func__, section_name);
       break;
     }
 
@@ -75,29 +75,26 @@
 
     if (record.vendor_id_source != DI_VENDOR_ID_SOURCE_BTSIG &&
         record.vendor_id_source != DI_VENDOR_ID_SOURCE_USBIF) {
-      LOG_ERROR(LOG_TAG,
-                "%s invalid vendor id source %d; ignoring DID record %d.",
+      LOG_ERROR("%s invalid vendor id source %d; ignoring DID record %d.",
                 __func__, record.vendor_id_source, i);
       continue;
     }
 
-    LOG_DEBUG(LOG_TAG, "Device ID record %d : %s", i,
+    LOG_DEBUG("Device ID record %d : %s", i,
               (record.primary_record ? "primary" : "not primary"));
-    LOG_DEBUG(LOG_TAG, "  vendorId            = %04x", record.vendor);
-    LOG_DEBUG(LOG_TAG, "  vendorIdSource      = %04x", record.vendor_id_source);
-    LOG_DEBUG(LOG_TAG, "  product             = %04x", record.product);
-    LOG_DEBUG(LOG_TAG, "  version             = %04x", record.version);
-    LOG_DEBUG(LOG_TAG, "  clientExecutableURL = %s",
-              record.client_executable_url);
-    LOG_DEBUG(LOG_TAG, "  serviceDescription  = %s",
-              record.service_description);
-    LOG_DEBUG(LOG_TAG, "  documentationURL    = %s", record.documentation_url);
+    LOG_DEBUG("  vendorId            = %04x", record.vendor);
+    LOG_DEBUG("  vendorIdSource      = %04x", record.vendor_id_source);
+    LOG_DEBUG("  product             = %04x", record.product);
+    LOG_DEBUG("  version             = %04x", record.version);
+    LOG_DEBUG("  clientExecutableURL = %s", record.client_executable_url);
+    LOG_DEBUG("  serviceDescription  = %s", record.service_description);
+    LOG_DEBUG("  documentationURL    = %s", record.documentation_url);
 
     uint32_t record_handle;
     tBTA_STATUS status = BTA_DmSetLocalDiRecord(&record, &record_handle);
     if (status != BTA_SUCCESS) {
-      LOG_ERROR(LOG_TAG, "%s unable to set device ID record %d: error %d.",
-                __func__, i, status);
+      LOG_ERROR("%s unable to set device ID record %d: error %d.", __func__, i,
+                status);
     }
   }
 }
diff --git a/main/bte_logmsg.cc b/main/bte_logmsg.cc
index 22c357d..71f6f4b 100644
--- a/main/bte_logmsg.cc
+++ b/main/bte_logmsg.cc
@@ -181,26 +181,31 @@
   vsnprintf(&buffer[MSG_BUFFER_OFFSET], BTE_LOG_MAX_SIZE, fmt_str, ap);
   va_end(ap);
 
+#undef LOG_TAG
+#define LOG_TAG bt_layer_tags[trace_layer]
+
   switch (TRACE_GET_TYPE(trace_set_mask)) {
     case TRACE_TYPE_ERROR:
-      LOG_ERROR(bt_layer_tags[trace_layer], "%s", buffer);
+      LOG_ERROR("%s", buffer);
       break;
     case TRACE_TYPE_WARNING:
-      LOG_WARN(bt_layer_tags[trace_layer], "%s", buffer);
+      LOG_WARN("%s", buffer);
       break;
     case TRACE_TYPE_API:
     case TRACE_TYPE_EVENT:
-      LOG_INFO(bt_layer_tags[trace_layer], "%s", buffer);
+      LOG_INFO("%s", buffer);
       break;
     case TRACE_TYPE_DEBUG:
-      LOG_DEBUG(bt_layer_tags[trace_layer], "%s", buffer);
+      LOG_DEBUG("%s", buffer);
       break;
     default:
       /* we should never get this */
-      LOG_ERROR(bt_layer_tags[trace_layer], "!BAD TRACE TYPE! %s", buffer);
+      LOG_ERROR("!BAD TRACE TYPE! %s", buffer);
       CHECK(TRACE_GET_TYPE(trace_set_mask) == TRACE_TYPE_ERROR);
       break;
   }
+#undef LOG_TAG
+#define LOG_TAG "bt_bte"
 }
 
 /* this function should go into BTAPP_DM for example */
@@ -230,8 +235,8 @@
     int value = config_get_int(*config, CONFIG_DEFAULT_SECTION,
                                functions->trc_name, -1);
     if (value != -1) functions->trace_level = value;
-    LOG_INFO(LOG_TAG, "BTE_InitTraceLevels -- %s : Level %d",
-             functions->trc_name, functions->trace_level);
+    LOG_INFO("BTE_InitTraceLevels -- %s : Level %d", functions->trc_name,
+             functions->trace_level);
     if (functions->p_f) functions->p_f(functions->trace_level);
   }
 }
@@ -239,7 +244,7 @@
 static future_t* init(void) {
   const stack_config_t* stack_config = stack_config_get_interface();
   if (!stack_config->get_trace_config_enabled()) {
-    LOG_INFO(LOG_TAG, "using compile default trace settings");
+    LOG_INFO("using compile default trace settings");
     return NULL;
   }
 
diff --git a/main/bte_main.cc b/main/bte_main.cc
index 1628976..829028b 100644
--- a/main/bte_main.cc
+++ b/main/bte_main.cc
@@ -27,30 +27,17 @@
 #define LOG_TAG "bt_main"
 
 #include <base/logging.h>
-#include <base/threading/thread.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <time.h>
 
 #include <hardware/bluetooth.h>
 
 #include "bt_common.h"
-#include "bt_hci_bdroid.h"
-#include "bt_utils.h"
-#include "bta_api.h"
 #include "btcore/include/module.h"
 #include "bte.h"
-#include "btif_common.h"
+#include "btif/include/btif_config.h"
 #include "btsnoop.h"
 #include "btu.h"
 #include "device/include/interop.h"
 #include "hci_layer.h"
-#include "hcimsgs.h"
-#include "osi/include/alarm.h"
-#include "osi/include/fixed_queue.h"
-#include "osi/include/future.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 #include "shim/hci_layer.h"
@@ -120,7 +107,7 @@
 
   hci = hci_layer_get_interface();
   if (!hci) {
-    LOG_ERROR(LOG_TAG, "%s could not get hci layer interface.", __func__);
+    LOG_ERROR("%s could not get hci layer interface.", __func__);
     return;
   }
 
@@ -158,10 +145,12 @@
   APPL_TRACE_DEBUG("%s", __func__);
 
   if (bluetooth::shim::is_gd_shim_enabled()) {
-    LOG_INFO(LOG_TAG, "%s Gd shim module enabled", __func__);
+    LOG_INFO("%s Gd shim module enabled", __func__);
+    module_shut_down(get_module(GD_IDLE_MODULE));
     module_start_up(get_module(GD_SHIM_MODULE));
-    module_start_up(get_module(GD_HCI_MODULE));
+    module_start_up(get_module(BTIF_CONFIG_MODULE));
   } else {
+    module_start_up(get_module(BTIF_CONFIG_MODULE));
     module_start_up(get_module(BTSNOOP_MODULE));
     module_start_up(get_module(HCI_MODULE));
   }
@@ -183,9 +172,9 @@
   APPL_TRACE_DEBUG("%s", __func__);
 
   if (bluetooth::shim::is_gd_shim_enabled()) {
-    LOG_INFO(LOG_TAG, "%s Gd shim module enabled", __func__);
-    module_shut_down(get_module(GD_HCI_MODULE));
+    LOG_INFO("%s Gd shim module enabled", __func__);
     module_shut_down(get_module(GD_SHIM_MODULE));
+    module_start_up(get_module(GD_IDLE_MODULE));
   } else {
     module_shut_down(get_module(HCI_MODULE));
     module_shut_down(get_module(BTSNOOP_MODULE));
diff --git a/main/shim/Android.bp b/main/shim/Android.bp
new file mode 100644
index 0000000..1f3e0ba
--- /dev/null
+++ b/main/shim/Android.bp
@@ -0,0 +1,17 @@
+filegroup {
+    name: "LibBluetoothShimSources",
+    srcs: [
+        "btif_dm.cc",
+        "btm.cc",
+        "btm_api.cc",
+        "controller.cc",
+        "config.cc",
+        "dumpsys.cc",
+        "entry.cc",
+        "hci_layer.cc",
+        "l2c_api.cc",
+        "l2cap.cc",
+        "shim.cc",
+        "stack.cc",
+    ]
+}
diff --git a/main/shim/btif_dm.cc b/main/shim/btif_dm.cc
new file mode 100644
index 0000000..adf128c
--- /dev/null
+++ b/main/shim/btif_dm.cc
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "bt_shim_btif_dm"
+
+#include "osi/include/log.h"
+
+#include "main/shim/btif_dm.h"
+#include "main/shim/entry.h"
+#include "main/shim/helpers.h"
+#include "security/security_module.h"
+#include "security/ui.h"
+
+using ::bluetooth::shim::GetSecurityModule;
+
+namespace bluetooth {
+namespace shim {
+
+class ShimUi : public security::UI {
+ public:
+  ~ShimUi() {}
+  void DisplayPairingPrompt(const bluetooth::hci::AddressWithType& address,
+                            std::string name) {
+    LOG_WARN("%s â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â–  TODO Unimplemented",
+             __func__);
+  }
+  void Cancel(const bluetooth::hci::AddressWithType& address) {
+    LOG_WARN("%s â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â–  TODO Unimplemented",
+             __func__);
+  }
+
+  void DisplayConfirmValue(const bluetooth::hci::AddressWithType& address,
+                           std::string name, uint32_t numeric_value) {
+    bt_bdname_t legacy_name{0};
+    memcpy(legacy_name.name, name.data(), name.length());
+    callback_(ToRawAddress(address.GetAddress()), legacy_name,
+              ((0x1F) << 8) /* COD_UNCLASSIFIED*/,
+              BT_SSP_VARIANT_PASSKEY_CONFIRMATION, numeric_value);
+  }
+
+  void DisplayYesNoDialog(const bluetooth::hci::AddressWithType& address,
+                          std::string name) {
+    bt_bdname_t legacy_name{0};
+    memcpy(legacy_name.name, name.data(), name.length());
+    callback_(ToRawAddress(address.GetAddress()), legacy_name,
+              ((0x1F) << 8) /* COD_UNCLASSIFIED*/, BT_SSP_VARIANT_CONSENT, 0);
+  }
+
+  void DisplayEnterPasskeyDialog(const bluetooth::hci::AddressWithType& address, std::string name) {
+    bt_bdname_t legacy_name{0};
+    memcpy(legacy_name.name, name.data(), name.length());
+    callback_(ToRawAddress(address.GetAddress()), legacy_name,
+              ((0x1F) << 8) /* COD_UNCLASSIFIED*/, BT_SSP_VARIANT_PASSKEY_ENTRY,
+              0);
+  }
+
+  void DisplayPasskey(const bluetooth::hci::AddressWithType& address, std::string name, uint32_t passkey) {
+    bt_bdname_t legacy_name{0};
+    memcpy(legacy_name.name, name.data(), name.length());
+    callback_(ToRawAddress(address.GetAddress()), legacy_name,
+              ((0x1F) << 8) /* COD_UNCLASSIFIED*/,
+              BT_SSP_VARIANT_PASSKEY_NOTIFICATION, passkey);
+  }
+
+  void SetLegacyCallback(std::function<void(RawAddress, bt_bdname_t, uint32_t, bt_ssp_variant_t, uint32_t)> callback) {
+    callback_ = callback;
+  }
+
+ private:
+  std::function<void(RawAddress, bt_bdname_t, uint32_t, bt_ssp_variant_t,
+                     uint32_t)>
+      callback_;
+};
+
+ShimUi ui;
+
+/**
+ * Sets handler to SecurityModule and provides callback to handler
+ */
+void BTIF_DM_SetUiCallback(std::function<void(RawAddress, bt_bdname_t, uint32_t, bt_ssp_variant_t, uint32_t)> callback) {
+  LOG_WARN("%s", __func__);
+  auto security_manager = bluetooth::shim::GetSecurityModule()->GetSecurityManager();
+  ui.SetLegacyCallback(callback);
+  security_manager->SetUserInterfaceHandler(&ui, bluetooth::shim::GetGdShimHandler());
+}
+
+class ShimBondListener : public security::ISecurityManagerListener {
+ public:
+  void SetLegacyCallbacks(std::function<void(RawAddress)> bond_state_bonding_cb,
+                          std::function<void(RawAddress)> bond_state_bonded_cb,
+                          std::function<void(RawAddress)> bond_state_none_cb) {
+    bond_state_bonding_cb_ = bond_state_bonding_cb;
+    bond_state_bonded_cb_ = bond_state_bonded_cb;
+    bond_state_none_cb_ = bond_state_none_cb;
+  }
+
+  void OnDeviceBonded(bluetooth::hci::AddressWithType device) override {
+    bond_state_bonded_cb_(RawAddress(device.GetAddress().address));
+  }
+
+  void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) override {
+    bond_state_none_cb_(RawAddress(device.GetAddress().address));
+  }
+
+  void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) override {
+    bond_state_none_cb_(RawAddress(device.GetAddress().address));
+  }
+
+  void OnEncryptionStateChanged(
+      EncryptionChangeView encryption_change_view) override {}
+
+  std::function<void(RawAddress)> bond_state_bonding_cb_;
+  std::function<void(RawAddress)> bond_state_bonded_cb_;
+  std::function<void(RawAddress)> bond_state_none_cb_;
+};
+
+ShimBondListener shim_bond_listener;
+
+void BTIF_RegisterBondStateChangeListener(
+    std::function<void(RawAddress)> bonding_cb,
+    std::function<void(RawAddress)> bonded_cb,
+    std::function<void(RawAddress)> none_cb) {
+  auto security_manager =
+      bluetooth::shim::GetSecurityModule()->GetSecurityManager();
+  shim_bond_listener.SetLegacyCallbacks(bonding_cb, bonded_cb, none_cb);
+  security_manager->RegisterCallbackListener(
+      &shim_bond_listener, bluetooth::shim::GetGdShimHandler());
+}
+
+void BTIF_DM_ssp_reply(const RawAddress bd_addr, uint8_t addr_type, bt_ssp_variant_t variant, uint8_t accept) {
+  hci::AddressWithType address = ToAddressWithType(bd_addr, addr_type);
+  auto security_manager = bluetooth::shim::GetSecurityModule()->GetSecurityManager();
+
+  if (variant == BT_SSP_VARIANT_PASSKEY_CONFIRMATION || variant == BT_SSP_VARIANT_CONSENT) {
+    security_manager->OnConfirmYesNo(address, accept);
+  } else {
+    //TODO:
+    // void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+    //  void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) override;
+    LOG_WARN(
+        "â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â–  Variant not implemented yet "
+        "%02x",
+        variant);
+  }
+}
+
+void BTIF_DM_pin_reply(const RawAddress bd_addr, uint8_t addr_type, uint8_t accept, uint8_t pin_len, bt_pin_code_t pin_code) {
+  hci::AddressWithType address = ToAddressWithType(bd_addr, addr_type);
+  auto security_manager = bluetooth::shim::GetSecurityModule()->GetSecurityManager();
+
+  if (!accept) {
+    LOG_WARN("This case is not implemented!!");
+    return;
+  }
+
+  uint32_t passkey = 0;
+  int multi[] = {100000, 10000, 1000, 100, 10, 1};
+  for (int i = 0; i < pin_len; i++) {
+    passkey += (multi[i] * (pin_code.pin[i] - '0'));
+  }
+
+  security_manager->OnPasskeyEntry(address, passkey);
+}
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/btif_dm.h b/main/shim/btif_dm.h
new file mode 100644
index 0000000..450038b
--- /dev/null
+++ b/main/shim/btif_dm.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <functional>
+
+#include "include/hardware/bluetooth.h"
+#include "types/raw_address.h"
+
+namespace bluetooth {
+namespace shim {
+
+/**
+ * Sets handler to SecurityModule and provides callback to handler
+ */
+void BTIF_DM_SetUiCallback(std::function<void(RawAddress, bt_bdname_t, uint32_t, bt_ssp_variant_t, uint32_t)> callback);
+void BTIF_DM_ssp_reply(const RawAddress bd_addr, uint8_t, bt_ssp_variant_t variant, uint8_t accept);
+void BTIF_DM_pin_reply(const RawAddress bd_addr, uint8_t, uint8_t, uint8_t, bt_pin_code_t);
+void BTIF_RegisterBondStateChangeListener(
+    std::function<void(RawAddress)> bond_state_bonding_cb,
+    std::function<void(RawAddress)> bond_state_bonded_cb,
+    std::function<void(RawAddress)> bond_state_none_cb);
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/btm.cc b/main/shim/btm.cc
index 4fa225d..f0ebcb3 100644
--- a/main/shim/btm.cc
+++ b/main/shim/btm.cc
@@ -17,26 +17,32 @@
 #define LOG_TAG "bt_shim_btm"
 
 #include <algorithm>
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
 #include <cstring>
+#include <mutex>
 
-#include "stack/btm/btm_int_types.h"
-
+#include "bta/include/bta_api.h"
 #include "main/shim/btm.h"
+#include "main/shim/controller.h"
 #include "main/shim/entry.h"
+#include "main/shim/helpers.h"
 #include "main/shim/shim.h"
-#include "osi/include/log.h"
+#include "stack/btm/btm_int_types.h"
+#include "types/raw_address.h"
+
+#include "gd/hci/le_advertising_manager.h"
+#include "gd/hci/le_scanning_manager.h"
+#include "gd/neighbor/connectability.h"
+#include "gd/neighbor/discoverability.h"
+#include "gd/neighbor/inquiry.h"
+#include "gd/neighbor/name.h"
+#include "gd/neighbor/page.h"
+#include "gd/security/security_module.h"
 
 extern tBTM_CB btm_cb;
 
-static constexpr size_t kMaxInquiryResultSize = 4096;
-static uint8_t inquiry_result_buf[kMaxInquiryResultSize];
-
-static int inquiry_type_ = 0;
-
-static constexpr uint8_t kInquiryResultMode = 0;
-static constexpr uint8_t kInquiryResultWithRssiMode = 1;
-static constexpr uint8_t kExtendedInquiryResultMode = 2;
-
 static constexpr size_t kRemoteDeviceNameLength = 248;
 
 static constexpr uint8_t kAdvDataInfoNotPresent = 0xff;
@@ -44,11 +50,12 @@
 static constexpr uint8_t kNotPeriodicAdvertisement = 0x00;
 
 static constexpr bool kActiveScanning = true;
-static constexpr bool kPassiveScanning = true;
+static constexpr bool kPassiveScanning = false;
+
+using BtmRemoteDeviceName = tBTM_REMOTE_DEV_NAME;
 
 extern void btm_process_cancel_complete(uint8_t status, uint8_t mode);
 extern void btm_process_inq_complete(uint8_t status, uint8_t result_type);
-extern void btm_process_inq_results(uint8_t* p, uint8_t result_mode);
 extern void btm_ble_process_adv_addr(RawAddress& raw_address,
                                      uint8_t* address_type);
 extern void btm_ble_process_adv_pkt_cont(
@@ -57,285 +64,499 @@
     int8_t tx_power, int8_t rssi, uint16_t periodic_adv_int, uint8_t data_len,
     uint8_t* data);
 
-using BtmRemoteDeviceName = tBTM_REMOTE_DEV_NAME;
+extern void btm_api_process_inquiry_result(const RawAddress& raw_address,
+                                           uint8_t page_scan_rep_mode,
+                                           DEV_CLASS device_class,
+                                           uint16_t clock_offset);
 
-bluetooth::shim::Btm::Btm() {}
+extern void btm_api_process_inquiry_result_with_rssi(RawAddress raw_address,
+                                                     uint8_t page_scan_rep_mode,
+                                                     DEV_CLASS device_class,
+                                                     uint16_t clock_offset,
+                                                     int8_t rssi);
 
-bluetooth::shim::Btm::~Btm() {}
+extern void btm_api_process_extended_inquiry_result(
+    RawAddress raw_address, uint8_t page_scan_rep_mode, DEV_CLASS device_class,
+    uint16_t clock_offset, int8_t rssi, const uint8_t* eir_data,
+    size_t eir_len);
 
-/**
- *
- */
-void bluetooth::shim::Btm::OnInquiryResult(std::vector<const uint8_t> result) {
-  CHECK(result.size() < kMaxInquiryResultSize);
+namespace bluetooth {
 
-  std::copy(result.begin(), result.end(), inquiry_result_buf);
-  btm_process_inq_results(inquiry_result_buf, kInquiryResultMode);
+namespace shim {
+
+constexpr int kAdvertisingReportBufferSize = 1024;
+
+struct ExtendedEventTypeOptions {
+  bool connectable{false};
+  bool scannable{false};
+  bool directed{false};
+  bool scan_response{false};
+  bool legacy{false};
+  bool continuing{false};
+  bool truncated{false};
+};
+
+constexpr uint16_t kBleEventConnectableBit =
+    (0x0001 << 0);  // BLE_EVT_CONNECTABLE_BIT
+constexpr uint16_t kBleEventScannableBit =
+    (0x0001 << 1);  // BLE_EVT_SCANNABLE_BIT
+constexpr uint16_t kBleEventDirectedBit =
+    (0x0001 << 2);  // BLE_EVT_DIRECTED_BIT
+constexpr uint16_t kBleEventScanResponseBit =
+    (0x0001 << 3);  // BLE_EVT_SCAN_RESPONSE_BIT
+constexpr uint16_t kBleEventLegacyBit = (0x0001 << 4);  // BLE_EVT_LEGACY_BIT
+constexpr uint16_t kBleEventIncompleteContinuing = (0x0001 << 5);
+constexpr uint16_t kBleEventIncompleteTruncated = (0x0001 << 6);
+
+static void TransformToExtendedEventType(uint16_t* extended_event_type,
+                                         ExtendedEventTypeOptions o) {
+  ASSERT(extended_event_type != nullptr);
+  *extended_event_type = (o.connectable ? kBleEventConnectableBit : 0) |
+                         (o.scannable ? kBleEventScannableBit : 0) |
+                         (o.directed ? kBleEventDirectedBit : 0) |
+                         (o.scan_response ? kBleEventScanResponseBit : 0) |
+                         (o.legacy ? kBleEventLegacyBit : 0) |
+                         (o.continuing ? kBleEventIncompleteContinuing : 0) |
+                         (o.truncated ? kBleEventIncompleteTruncated : 0);
 }
 
-void bluetooth::shim::Btm::OnInquiryResultWithRssi(
-    std::vector<const uint8_t> result) {
-  CHECK(result.size() < kMaxInquiryResultSize);
-
-  std::copy(result.begin(), result.end(), inquiry_result_buf);
-  btm_process_inq_results(inquiry_result_buf, kInquiryResultWithRssiMode);
+bool Btm::ReadRemoteName::Start(RawAddress raw_address) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  if (in_progress_) {
+    return false;
+  }
+  raw_address_ = raw_address;
+  in_progress_ = true;
+  return true;
 }
 
-void bluetooth::shim::Btm::OnExtendedInquiryResult(
-    std::vector<const uint8_t> result) {
-  CHECK(result.size() < kMaxInquiryResultSize);
-
-  std::copy(result.begin(), result.end(), inquiry_result_buf);
-  btm_process_inq_results(inquiry_result_buf, kExtendedInquiryResultMode);
+void Btm::ReadRemoteName::Stop() {
+  std::unique_lock<std::mutex> lock(mutex_);
+  raw_address_ = RawAddress::kEmpty;
+  in_progress_ = false;
 }
 
-void bluetooth::shim::Btm::OnInquiryComplete(uint16_t status) {
-  btm_process_inq_complete(status, inquiry_type_);
+bool Btm::ReadRemoteName::IsInProgress() const { return in_progress_; }
+std::string Btm::ReadRemoteName::AddressString() const {
+  return raw_address_.ToString();
 }
 
-bool bluetooth::shim::Btm::SetInquiryFilter(uint8_t mode, uint8_t type,
-                                            tBTM_INQ_FILT_COND data) {
+void Btm::ScanningCallbacks::on_advertisements(
+    std::vector<std::shared_ptr<hci::LeReport>> reports) {
+  for (auto le_report : reports) {
+    uint8_t address_type = static_cast<uint8_t>(le_report->address_type_);
+    uint16_t extended_event_type = 0;
+    uint8_t* report_data = nullptr;
+    size_t report_len = 0;
+
+    uint8_t advertising_data_buffer[kAdvertisingReportBufferSize];
+    // Copy gap data, if any, into temporary buffer as payload for legacy
+    // stack.
+    if (!le_report->gap_data_.empty()) {
+      bzero(advertising_data_buffer, kAdvertisingReportBufferSize);
+      uint8_t* p = advertising_data_buffer;
+      for (auto gap_data : le_report->gap_data_) {
+        *p++ = gap_data.data_.size() + sizeof(gap_data.data_type_);
+        *p++ = static_cast<uint8_t>(gap_data.data_type_);
+        p = (uint8_t*)memcpy(p, &gap_data.data_[0], gap_data.data_.size()) +
+            gap_data.data_.size();
+      }
+      report_data = advertising_data_buffer;
+      report_len = p - report_data;
+    }
+
+    switch (le_report->GetReportType()) {
+      case hci::LeReport::ReportType::ADVERTISING_EVENT: {
+        switch (le_report->advertising_event_type_) {
+          case hci::AdvertisingEventType::ADV_IND:
+            TransformToExtendedEventType(
+                &extended_event_type,
+                {.connectable = true, .scannable = true, .legacy = true});
+            break;
+          case hci::AdvertisingEventType::ADV_DIRECT_IND:
+            TransformToExtendedEventType(
+                &extended_event_type,
+                {.connectable = true, .directed = true, .legacy = true});
+            break;
+          case hci::AdvertisingEventType::ADV_SCAN_IND:
+            TransformToExtendedEventType(&extended_event_type,
+                                         {.scannable = true, .legacy = true});
+            break;
+          case hci::AdvertisingEventType::ADV_NONCONN_IND:
+            TransformToExtendedEventType(&extended_event_type,
+                                         {.legacy = true});
+            break;
+          case hci::AdvertisingEventType::SCAN_RESPONSE:
+            TransformToExtendedEventType(&extended_event_type,
+                                         {.connectable = true,
+                                          .scannable = true,
+                                          .scan_response = true,
+                                          .legacy = true});
+            break;
+          default:
+            LOG_WARN(
+                "%s Unsupported event type:%s", __func__,
+                AdvertisingEventTypeText(le_report->advertising_event_type_)
+                    .c_str());
+            return;
+        }
+
+        RawAddress raw_address = ToRawAddress(le_report->address_);
+
+        btm_ble_process_adv_addr(raw_address, &address_type);
+        btm_ble_process_adv_pkt_cont(
+            extended_event_type, address_type, raw_address, kPhyConnectionLe1M,
+            kPhyConnectionNone, kAdvDataInfoNotPresent,
+            kTxPowerInformationNotPresent, le_report->rssi_,
+            kNotPeriodicAdvertisement, report_len, report_data);
+      } break;
+
+      case hci::LeReport::ReportType::DIRECTED_ADVERTISING_EVENT:
+        LOG_WARN("%s Directed advertising is unsupported from device:%s",
+                 __func__, le_report->address_.ToString().c_str());
+        break;
+
+      case hci::LeReport::ReportType::EXTENDED_ADVERTISING_EVENT: {
+        std::shared_ptr<hci::ExtendedLeReport> extended_le_report =
+            std::static_pointer_cast<hci::ExtendedLeReport>(le_report);
+        TransformToExtendedEventType(
+            &extended_event_type,
+            {.connectable = extended_le_report->connectable_,
+             .scannable = extended_le_report->scannable_,
+             .directed = extended_le_report->directed_,
+             .scan_response = extended_le_report->scan_response_,
+             .legacy = false,
+             .continuing = !extended_le_report->complete_,
+             .truncated = extended_le_report->truncated_});
+        RawAddress raw_address = ToRawAddress(le_report->address_);
+        if (address_type != BLE_ADDR_ANONYMOUS) {
+          btm_ble_process_adv_addr(raw_address, &address_type);
+        }
+        btm_ble_process_adv_pkt_cont(
+            extended_event_type, address_type, raw_address, kPhyConnectionLe1M,
+            kPhyConnectionNone, kAdvDataInfoNotPresent,
+            kTxPowerInformationNotPresent, le_report->rssi_,
+            kNotPeriodicAdvertisement, report_len, report_data);
+
+      } break;
+    }
+  }
+}
+
+void Btm::ScanningCallbacks::on_timeout() {
+  LOG_WARN("%s Scanning timeout", __func__);
+}
+os::Handler* Btm::ScanningCallbacks::Handler() {
+  return shim::GetGdShimHandler();
+}
+
+Btm::Btm(os::Handler* handler, neighbor::InquiryModule* inquiry)
+    : scanning_timer_(handler), observing_timer_(handler) {
+  ASSERT(handler != nullptr);
+  ASSERT(inquiry != nullptr);
+  bluetooth::neighbor::InquiryCallbacks inquiry_callbacks = {
+      .result = std::bind(&Btm::OnInquiryResult, this, std::placeholders::_1),
+      .result_with_rssi =
+          std::bind(&Btm::OnInquiryResultWithRssi, this, std::placeholders::_1),
+      .extended_result =
+          std::bind(&Btm::OnExtendedInquiryResult, this, std::placeholders::_1),
+      .complete =
+          std::bind(&Btm::OnInquiryComplete, this, std::placeholders::_1)};
+  inquiry->RegisterCallbacks(std::move(inquiry_callbacks));
+}
+
+void Btm::OnInquiryResult(bluetooth::hci::InquiryResultView view) {
+  for (auto& response : view.GetInquiryResults()) {
+    btm_api_process_inquiry_result(
+        ToRawAddress(response.bd_addr_),
+        static_cast<uint8_t>(response.page_scan_repetition_mode_),
+        response.class_of_device_.cod, response.clock_offset_);
+  }
+}
+
+void Btm::OnInquiryResultWithRssi(
+    bluetooth::hci::InquiryResultWithRssiView view) {
+  for (auto& response : view.GetInquiryResults()) {
+    btm_api_process_inquiry_result_with_rssi(
+        ToRawAddress(response.address_),
+        static_cast<uint8_t>(response.page_scan_repetition_mode_),
+        response.class_of_device_.cod, response.clock_offset_, response.rssi_);
+  }
+}
+
+void Btm::OnExtendedInquiryResult(
+    bluetooth::hci::ExtendedInquiryResultView view) {
+  constexpr size_t kMaxExtendedInquiryResponse = 240;
+  uint8_t gap_data_buffer[kMaxExtendedInquiryResponse];
+  uint8_t* data = nullptr;
+  size_t data_len = 0;
+
+  if (!view.GetExtendedInquiryResponse().empty()) {
+    bzero(gap_data_buffer, sizeof(gap_data_buffer));
+    uint8_t* p = gap_data_buffer;
+    for (auto gap_data : view.GetExtendedInquiryResponse()) {
+      *p++ = gap_data.data_.size() + sizeof(gap_data.data_type_);
+      *p++ = static_cast<uint8_t>(gap_data.data_type_);
+      p = (uint8_t*)memcpy(p, &gap_data.data_[0], gap_data.data_.size()) +
+          gap_data.data_.size();
+    }
+    data = gap_data_buffer;
+    data_len = p - data;
+  }
+
+  btm_api_process_extended_inquiry_result(
+      ToRawAddress(view.GetAddress()),
+      static_cast<uint8_t>(view.GetPageScanRepetitionMode()),
+      view.GetClassOfDevice().cod, view.GetClockOffset(), view.GetRssi(), data,
+      data_len);
+}
+
+void Btm::OnInquiryComplete(bluetooth::hci::ErrorCode status) {
+  limited_inquiry_active_ = false;
+  general_inquiry_active_ = false;
+  legacy_inquiry_complete_callback_((static_cast<uint16_t>(status) == 0)
+                                        ? (BTM_SUCCESS)
+                                        : (BTM_ERR_PROCESSING),
+                                    active_inquiry_mode_);
+
+  active_inquiry_mode_ = kInquiryModeOff;
+}
+
+bool Btm::SetInquiryFilter(uint8_t mode, uint8_t type,
+                           tBTM_INQ_FILT_COND data) {
   switch (mode) {
     case kInquiryModeOff:
       break;
     case kLimitedInquiryMode:
-      LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+      LOG_WARN("UNIMPLEMENTED %s", __func__);
       break;
     case kGeneralInquiryMode:
-      LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+      LOG_WARN("UNIMPLEMENTED %s", __func__);
       break;
     default:
-      LOG_WARN(LOG_TAG, "%s Unknown inquiry mode:%d", __func__, mode);
+      LOG_WARN("%s Unknown inquiry mode:%d", __func__, mode);
       return false;
   }
   return true;
 }
 
-void bluetooth::shim::Btm::SetFilterInquiryOnAddress() {
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+void Btm::SetFilterInquiryOnAddress() {
+  LOG_WARN("UNIMPLEMENTED %s", __func__);
 }
 
-void bluetooth::shim::Btm::SetFilterInquiryOnDevice() {
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+void Btm::SetFilterInquiryOnDevice() { LOG_WARN("UNIMPLEMENTED %s", __func__); }
+
+void Btm::ClearInquiryFilter() { LOG_WARN("UNIMPLEMENTED %s", __func__); }
+
+void Btm::SetStandardInquiryResultMode() {
+  GetInquiry()->SetStandardInquiryResultMode();
 }
 
-void bluetooth::shim::Btm::ClearInquiryFilter() {
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+void Btm::SetInquiryWithRssiResultMode() {
+  GetInquiry()->SetInquiryWithRssiResultMode();
 }
 
-bool bluetooth::shim::Btm::SetStandardInquiryResultMode() {
-  bluetooth::shim::GetInquiry()->SetStandardInquiryResultMode();
-  return true;
+void Btm::SetExtendedInquiryResultMode() {
+  GetInquiry()->SetExtendedInquiryResultMode();
 }
 
-bool bluetooth::shim::Btm::SetInquiryWithRssiResultMode() {
-  bluetooth::shim::GetInquiry()->SetInquiryWithRssiResultMode();
-  return true;
-}
+void Btm::SetInterlacedInquiryScan() { GetInquiry()->SetInterlacedScan(); }
 
-bool bluetooth::shim::Btm::SetExtendedInquiryResultMode() {
-  bluetooth::shim::GetInquiry()->SetExtendedInquiryResultMode();
-  return true;
-}
+void Btm::SetStandardInquiryScan() { GetInquiry()->SetStandardScan(); }
 
-void bluetooth::shim::Btm::SetInterlacedInquiryScan() {
-  bluetooth::shim::GetInquiry()->SetInterlacedScan();
-}
-
-void bluetooth::shim::Btm::SetStandardInquiryScan() {
-  bluetooth::shim::GetInquiry()->SetStandardScan();
-}
-
-bool bluetooth::shim::Btm::IsInterlacedScanSupported() const {
-  // TODO(cmanton) This is a controller query
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  return true;
+bool Btm::IsInterlacedScanSupported() const {
+  return controller_get_interface()->supports_interlaced_inquiry_scan();
 }
 
 /**
  * One shot inquiry
  */
-bool bluetooth::shim::Btm::StartInquiry(uint8_t mode, uint8_t duration,
-                                        uint8_t max_responses) {
+bool Btm::StartInquiry(
+    uint8_t mode, uint8_t duration, uint8_t max_responses,
+    LegacyInquiryCompleteCallback legacy_inquiry_complete_callback) {
   switch (mode) {
     case kInquiryModeOff:
-      LOG_DEBUG(LOG_TAG, "%s Stopping inquiry mode", __func__);
-      bluetooth::shim::GetInquiry()->StopInquiry();
-      bluetooth::shim::GetInquiry()->UnregisterInquiryResult();
-      bluetooth::shim::GetInquiry()->UnregisterInquiryResultWithRssi();
-      bluetooth::shim::GetInquiry()->UnregisterExtendedInquiryResult();
-      bluetooth::shim::GetInquiry()->UnregisterInquiryComplete();
+      LOG_DEBUG("%s Stopping inquiry mode", __func__);
+      if (limited_inquiry_active_ || general_inquiry_active_) {
+        GetInquiry()->StopInquiry();
+        limited_inquiry_active_ = false;
+        general_inquiry_active_ = false;
+      }
+      active_inquiry_mode_ = kInquiryModeOff;
       break;
 
     case kLimitedInquiryMode:
-    case kGeneralInquiryMode:
-      bluetooth::shim::GetInquiry()->RegisterInquiryResult(
-          std::bind(&Btm::OnInquiryResult, this, std::placeholders::_1));
-      bluetooth::shim::GetInquiry()->RegisterInquiryResultWithRssi(std::bind(
-          &Btm::OnInquiryResultWithRssi, this, std::placeholders::_1));
-      bluetooth::shim::GetInquiry()->RegisterExtendedInquiryResult(std::bind(
-          &Btm::OnExtendedInquiryResult, this, std::placeholders::_1));
-      bluetooth::shim::GetInquiry()->RegisterInquiryComplete(
-          std::bind(&Btm::OnInquiryComplete, this, std::placeholders::_1));
-
+    case kGeneralInquiryMode: {
       if (mode == kLimitedInquiryMode) {
         LOG_DEBUG(
-            LOG_TAG,
+
             "%s Starting limited inquiry mode duration:%hhd max responses:%hhd",
             __func__, duration, max_responses);
-        bluetooth::shim::GetInquiry()->StartLimitedInquiry(duration,
-                                                           max_responses);
+        limited_inquiry_active_ = true;
+        GetInquiry()->StartLimitedInquiry(duration, max_responses);
+        active_inquiry_mode_ = kLimitedInquiryMode;
       } else {
         LOG_DEBUG(
-            LOG_TAG,
+
             "%s Starting general inquiry mode duration:%hhd max responses:%hhd",
             __func__, duration, max_responses);
-        bluetooth::shim::GetInquiry()->StartGeneralInquiry(duration,
-                                                           max_responses);
+        general_inquiry_active_ = true;
+        GetInquiry()->StartGeneralInquiry(duration, max_responses);
+        legacy_inquiry_complete_callback_ = legacy_inquiry_complete_callback;
       }
-      break;
+    } break;
 
     default:
-      LOG_WARN(LOG_TAG, "%s Unknown inquiry mode:%d", __func__, mode);
+      LOG_WARN("%s Unknown inquiry mode:%d", __func__, mode);
       return false;
   }
   return true;
 }
 
-void bluetooth::shim::Btm::CancelInquiry() {
-  bluetooth::shim::GetInquiry()->StopInquiry();
+void Btm::CancelInquiry() {
+  LOG_DEBUG("%s", __func__);
+  if (limited_inquiry_active_ || general_inquiry_active_) {
+    GetInquiry()->StopInquiry();
+    limited_inquiry_active_ = false;
+    general_inquiry_active_ = false;
+  }
 }
 
-bool bluetooth::shim::Btm::IsInquiryActive() const {
+bool Btm::IsInquiryActive() const {
   return IsGeneralInquiryActive() || IsLimitedInquiryActive();
 }
 
-bool bluetooth::shim::Btm::IsGeneralInquiryActive() const {
-  return bluetooth::shim::GetInquiry()->IsGeneralInquiryActive();
-}
+bool Btm::IsGeneralInquiryActive() const { return general_inquiry_active_; }
 
-bool bluetooth::shim::Btm::IsLimitedInquiryActive() const {
-  return bluetooth::shim::GetInquiry()->IsLimitedInquiryActive();
-}
+bool Btm::IsLimitedInquiryActive() const { return limited_inquiry_active_; }
 
 /**
  * Periodic
  */
-bool bluetooth::shim::Btm::StartPeriodicInquiry(
-    uint8_t mode, uint8_t duration, uint8_t max_responses, uint16_t max_delay,
-    uint16_t min_delay, tBTM_INQ_RESULTS_CB* p_results_cb) {
+bool Btm::StartPeriodicInquiry(uint8_t mode, uint8_t duration,
+                               uint8_t max_responses, uint16_t max_delay,
+                               uint16_t min_delay,
+                               tBTM_INQ_RESULTS_CB* p_results_cb) {
   switch (mode) {
     case kInquiryModeOff:
-      bluetooth::shim::GetInquiry()->StopPeriodicInquiry();
+      limited_periodic_inquiry_active_ = false;
+      general_periodic_inquiry_active_ = false;
+      GetInquiry()->StopPeriodicInquiry();
       break;
 
     case kLimitedInquiryMode:
-    case kGeneralInquiryMode:
+    case kGeneralInquiryMode: {
       if (mode == kLimitedInquiryMode) {
-        LOG_DEBUG(LOG_TAG, "%s Starting limited periodic inquiry mode",
-                  __func__);
-        bluetooth::shim::GetInquiry()->StartLimitedPeriodicInquiry(
-            duration, max_responses, max_delay, min_delay);
+        LOG_DEBUG("%s Starting limited periodic inquiry mode", __func__);
+        limited_periodic_inquiry_active_ = true;
+        GetInquiry()->StartLimitedPeriodicInquiry(duration, max_responses,
+                                                  max_delay, min_delay);
       } else {
-        LOG_DEBUG(LOG_TAG, "%s Starting general periodic inquiry mode",
-                  __func__);
-        bluetooth::shim::GetInquiry()->StartGeneralPeriodicInquiry(
-            duration, max_responses, max_delay, min_delay);
+        LOG_DEBUG("%s Starting general periodic inquiry mode", __func__);
+        general_periodic_inquiry_active_ = true;
+        GetInquiry()->StartGeneralPeriodicInquiry(duration, max_responses,
+                                                  max_delay, min_delay);
       }
-      break;
+    } break;
 
     default:
-      LOG_WARN(LOG_TAG, "%s Unknown inquiry mode:%d", __func__, mode);
+      LOG_WARN("%s Unknown inquiry mode:%d", __func__, mode);
       return false;
   }
   return true;
 }
 
-void bluetooth::shim::Btm::CancelPeriodicInquiry() {
-  bluetooth::shim::GetInquiry()->StopPeriodicInquiry();
+void Btm::CancelPeriodicInquiry() {
+  limited_periodic_inquiry_active_ = false;
+  general_periodic_inquiry_active_ = false;
+  GetInquiry()->StopPeriodicInquiry();
 }
 
-bool bluetooth::shim::Btm::IsGeneralPeriodicInquiryActive() const {
-  return bluetooth::shim::GetInquiry()->IsGeneralPeriodicInquiryActive();
+bool Btm::IsGeneralPeriodicInquiryActive() const {
+  return general_periodic_inquiry_active_;
 }
 
-bool bluetooth::shim::Btm::IsLimitedPeriodicInquiryActive() const {
-  return bluetooth::shim::GetInquiry()->IsLimitedPeriodicInquiryActive();
+bool Btm::IsLimitedPeriodicInquiryActive() const {
+  return limited_periodic_inquiry_active_;
 }
 
 /**
  * Discoverability
  */
-void bluetooth::shim::Btm::SetClassicGeneralDiscoverability(uint16_t window,
-                                                            uint16_t interval) {
-  bluetooth::shim::GetInquiry()->SetScanActivity(interval, window);
-  bluetooth::shim::GetDiscoverability()->StartGeneralDiscoverability();
+
+bluetooth::neighbor::ScanParameters params_{
+    .interval = 0,
+    .window = 0,
+};
+
+void Btm::SetClassicGeneralDiscoverability(uint16_t window, uint16_t interval) {
+  params_.window = window;
+  params_.interval = interval;
+
+  GetInquiry()->SetScanActivity(params_);
+  GetDiscoverability()->StartGeneralDiscoverability();
 }
 
-void bluetooth::shim::Btm::SetClassicLimitedDiscoverability(uint16_t window,
-                                                            uint16_t interval) {
-  bluetooth::shim::GetInquiry()->SetScanActivity(interval, window);
-  bluetooth::shim::GetDiscoverability()->StartLimitedDiscoverability();
+void Btm::SetClassicLimitedDiscoverability(uint16_t window, uint16_t interval) {
+  params_.window = window;
+  params_.interval = interval;
+  GetInquiry()->SetScanActivity(params_);
+  GetDiscoverability()->StartLimitedDiscoverability();
 }
 
-void bluetooth::shim::Btm::SetClassicDiscoverabilityOff() {
-  bluetooth::shim::GetDiscoverability()->StopDiscoverability();
+void Btm::SetClassicDiscoverabilityOff() {
+  GetDiscoverability()->StopDiscoverability();
 }
 
-DiscoverabilityState bluetooth::shim::Btm::GetClassicDiscoverabilityState()
-    const {
-  DiscoverabilityState state{.mode = BTM_NON_DISCOVERABLE};
-  bluetooth::shim::GetInquiry()->GetScanActivity(state.interval, state.window);
+DiscoverabilityState Btm::GetClassicDiscoverabilityState() const {
+  DiscoverabilityState state{.mode = BTM_NON_DISCOVERABLE,
+                             .interval = params_.interval,
+                             .window = params_.window};
 
-  if (bluetooth::shim::GetDiscoverability()
-          ->IsGeneralDiscoverabilityEnabled()) {
+  if (GetDiscoverability()->IsGeneralDiscoverabilityEnabled()) {
     state.mode = BTM_GENERAL_DISCOVERABLE;
-  } else if (bluetooth::shim::GetDiscoverability()
-                 ->IsLimitedDiscoverabilityEnabled()) {
+  } else if (GetDiscoverability()->IsLimitedDiscoverabilityEnabled()) {
     state.mode = BTM_LIMITED_DISCOVERABLE;
   }
   return state;
 }
 
-void bluetooth::shim::Btm::SetLeGeneralDiscoverability() {
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+void Btm::SetLeGeneralDiscoverability() {
+  LOG_WARN("UNIMPLEMENTED %s", __func__);
 }
 
-void bluetooth::shim::Btm::SetLeLimitedDiscoverability() {
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+void Btm::SetLeLimitedDiscoverability() {
+  LOG_WARN("UNIMPLEMENTED %s", __func__);
 }
 
-void bluetooth::shim::Btm::SetLeDiscoverabilityOff() {
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-}
+void Btm::SetLeDiscoverabilityOff() { LOG_WARN("UNIMPLEMENTED %s", __func__); }
 
-DiscoverabilityState bluetooth::shim::Btm::GetLeDiscoverabilityState() const {
+DiscoverabilityState Btm::GetLeDiscoverabilityState() const {
   DiscoverabilityState state{
       .mode = kDiscoverableModeOff,
       .interval = 0,
       .window = 0,
   };
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_WARN("UNIMPLEMENTED %s", __func__);
   return state;
 }
 
 /**
  * Connectability
  */
-void bluetooth::shim::Btm::SetClassicConnectibleOn() {
-  bluetooth::shim::GetConnectability()->StartConnectability();
+void Btm::SetClassicConnectibleOn() {
+  GetConnectability()->StartConnectability();
 }
 
-void bluetooth::shim::Btm::SetClassicConnectibleOff() {
-  bluetooth::shim::GetConnectability()->StopConnectability();
+void Btm::SetClassicConnectibleOff() {
+  GetConnectability()->StopConnectability();
 }
 
-ConnectabilityState bluetooth::shim::Btm::GetClassicConnectabilityState()
-    const {
-  ConnectabilityState state;
-  bluetooth::shim::GetPage()->GetScanActivity(state.interval, state.window);
+ConnectabilityState Btm::GetClassicConnectabilityState() const {
+  ConnectabilityState state{.interval = params_.interval,
+                            .window = params_.window};
 
-  if (bluetooth::shim::GetConnectability()->IsConnectable()) {
+  if (GetConnectability()->IsConnectable()) {
     state.mode = BTM_CONNECTABLE;
   } else {
     state.mode = BTM_NON_CONNECTABLE;
@@ -343,189 +564,230 @@
   return state;
 }
 
-void bluetooth::shim::Btm::SetInterlacedPageScan() {
-  bluetooth::shim::GetPage()->SetInterlacedScan();
-}
+void Btm::SetInterlacedPageScan() { GetPage()->SetInterlacedScan(); }
 
-void bluetooth::shim::Btm::SetStandardPageScan() {
-  bluetooth::shim::GetPage()->SetStandardScan();
-}
+void Btm::SetStandardPageScan() { GetPage()->SetStandardScan(); }
 
-void bluetooth::shim::Btm::SetLeConnectibleOn() {
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-}
+void Btm::SetLeConnectibleOn() { LOG_WARN("UNIMPLEMENTED %s", __func__); }
 
-void bluetooth::shim::Btm::SetLeConnectibleOff() {
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-}
+void Btm::SetLeConnectibleOff() { LOG_WARN("UNIMPLEMENTED %s", __func__); }
 
-ConnectabilityState bluetooth::shim::Btm::GetLeConnectabilityState() const {
+ConnectabilityState Btm::GetLeConnectabilityState() const {
   ConnectabilityState state{
       .mode = kConnectibleModeOff,
       .interval = 0,
       .window = 0,
   };
-  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_WARN("UNIMPLEMENTED %s", __func__);
   return state;
 }
 
-bool bluetooth::shim::Btm::IsLeAclConnected(
-    const RawAddress& raw_address) const {
+bool Btm::IsLeAclConnected(const RawAddress& raw_address) const {
   // TODO(cmanton) Check current acl's for this address and indicate if there is
   // an LE option.  For now ignore and default to classic.
-  LOG_INFO(LOG_TAG, "%s Le acl connection check is temporarily unsupported",
-           __func__);
+  LOG_INFO("%s Le acl connection check is temporarily unsupported", __func__);
   return false;
 }
 
-bluetooth::shim::BtmStatus bluetooth::shim::Btm::ReadClassicRemoteDeviceName(
-    const RawAddress& raw_address, tBTM_CMPL_CB* callback) {
+BtmStatus Btm::ReadClassicRemoteDeviceName(const RawAddress& raw_address,
+                                           tBTM_CMPL_CB* callback) {
   if (!CheckClassicAclLink(raw_address)) {
-    return bluetooth::shim::BTM_UNKNOWN_ADDR;
+    return BTM_UNKNOWN_ADDR;
   }
 
   if (!classic_read_remote_name_.Start(raw_address)) {
-    LOG_INFO(LOG_TAG, "%s Read remote name is currently busy address:%s",
-             __func__, raw_address.ToString().c_str());
-    return bluetooth::shim::BTM_BUSY;
+    LOG_INFO("%s Read remote name is currently busy address:%s", __func__,
+             raw_address.ToString().c_str());
+    return BTM_BUSY;
   }
 
-  LOG_DEBUG(LOG_TAG, "%s Start read name from address:%s", __func__,
+  LOG_DEBUG("%s Start read name from address:%s", __func__,
             raw_address.ToString().c_str());
-  bluetooth::shim::GetName()->ReadRemoteNameRequest(
-      classic_read_remote_name_.AddressString(),
-      [this, callback](
-          std::string address_string, uint8_t hci_status,
-          std::array<uint8_t, kRemoteDeviceNameLength> remote_name) {
-        RawAddress raw_address;
-        RawAddress::FromString(address_string, raw_address);
+  GetName()->ReadRemoteNameRequest(
+      ToGdAddress(raw_address), hci::PageScanRepetitionMode::R1,
+      0 /* clock_offset */, hci::ClockOffsetValid::INVALID,
 
-        BtmRemoteDeviceName name{
-            .status = (hci_status == 0) ? (BTM_SUCCESS) : (BTM_BAD_VALUE_RET),
-            .bd_addr = raw_address,
-            .length = kRemoteDeviceNameLength,
-        };
-        std::copy(remote_name.begin(), remote_name.end(), name.remote_bd_name);
-        LOG_DEBUG(LOG_TAG, "%s Finish read name from address:%s name:%s",
-                  __func__, address_string.c_str(), name.remote_bd_name);
-        callback(&name);
-        classic_read_remote_name_.Stop();
-      });
-  return bluetooth::shim::BTM_CMD_STARTED;
+      base::Bind(
+          [](tBTM_CMPL_CB* callback, ReadRemoteName* classic_read_remote_name,
+             hci::ErrorCode status, hci::Address address,
+             std::array<uint8_t, kRemoteDeviceNameLength> remote_name) {
+            RawAddress raw_address = ToRawAddress(address);
+
+            BtmRemoteDeviceName name{
+                .status = (static_cast<uint8_t>(status) == 0)
+                              ? (BTM_SUCCESS)
+                              : (BTM_BAD_VALUE_RET),
+                .bd_addr = raw_address,
+                .length = kRemoteDeviceNameLength,
+            };
+            std::copy(remote_name.begin(), remote_name.end(),
+                      name.remote_bd_name);
+            LOG_DEBUG("%s Finish read name from address:%s name:%s", __func__,
+                      address.ToString().c_str(), name.remote_bd_name);
+            callback(&name);
+            classic_read_remote_name->Stop();
+          },
+          callback, &classic_read_remote_name_),
+      GetGdShimHandler());
+  return BTM_CMD_STARTED;
 }
 
-bluetooth::shim::BtmStatus bluetooth::shim::Btm::ReadLeRemoteDeviceName(
-    const RawAddress& raw_address, tBTM_CMPL_CB* callback) {
+BtmStatus Btm::ReadLeRemoteDeviceName(const RawAddress& raw_address,
+                                      tBTM_CMPL_CB* callback) {
   if (!CheckLeAclLink(raw_address)) {
-    return bluetooth::shim::BTM_UNKNOWN_ADDR;
+    return BTM_UNKNOWN_ADDR;
   }
 
   if (!le_read_remote_name_.Start(raw_address)) {
-    return bluetooth::shim::BTM_BUSY;
+    return BTM_BUSY;
   }
 
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s need access to GATT module", __func__);
-  return bluetooth::shim::BTM_UNKNOWN_ADDR;
+  LOG_INFO("UNIMPLEMENTED %s need access to GATT module", __func__);
+  return BTM_UNKNOWN_ADDR;
 }
 
-bluetooth::shim::BtmStatus
-bluetooth::shim::Btm::CancelAllReadRemoteDeviceName() {
+BtmStatus Btm::CancelAllReadRemoteDeviceName() {
   if (classic_read_remote_name_.IsInProgress() ||
       le_read_remote_name_.IsInProgress()) {
     if (classic_read_remote_name_.IsInProgress()) {
-      bluetooth::shim::GetName()->CancelRemoteNameRequest(
-          classic_read_remote_name_.AddressString(),
-          [this](std::string address_string, uint8_t status) {
-            classic_read_remote_name_.Stop();
-          });
+      hci::Address address;
+      hci::Address::FromString(classic_read_remote_name_.AddressString(),
+                               address);
+
+      GetName()->CancelRemoteNameRequest(
+          address,
+          common::BindOnce(
+              [](ReadRemoteName* classic_read_remote_name,
+                 hci::ErrorCode status,
+                 hci::Address address) { classic_read_remote_name->Stop(); },
+              &classic_read_remote_name_),
+          GetGdShimHandler());
     }
     if (le_read_remote_name_.IsInProgress()) {
-      LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s need access to GATT module",
-               __func__);
+      LOG_INFO("UNIMPLEMENTED %s need access to GATT module", __func__);
     }
-    return bluetooth::shim::BTM_UNKNOWN_ADDR;
+    return BTM_UNKNOWN_ADDR;
   }
-  LOG_INFO(LOG_TAG,
-           "%s Cancelling classic remote device name without one in progress",
+  LOG_WARN("%s Cancelling classic remote device name without one in progress",
            __func__);
-  return bluetooth::shim::BTM_WRONG_MODE;
+  return BTM_WRONG_MODE;
 }
 
-void bluetooth::shim::Btm::StartAdvertising() {
-  bluetooth::shim::GetAdvertising()->StartAdvertising();
+void Btm::StartAdvertising() {
+  if (advertiser_id_ == hci::LeAdvertisingManager::kInvalidId) {
+    LOG_WARN("%s Already advertising; please stop prior to starting again",
+             __func__);
+    return;
+  }
+
+  hci::AdvertisingConfig config = {};
+  advertiser_id_ = GetAdvertising()->CreateAdvertiser(
+      config, common::Bind([](hci::Address, hci::AddressType) { /*OnScan*/ }),
+      common::Bind([](hci::ErrorCode, uint8_t, uint8_t) { /*OnTerminated*/ }),
+      GetGdShimHandler());
+  if (advertiser_id_ == hci::LeAdvertisingManager::kInvalidId) {
+    LOG_WARN("%s Unable to start advertising", __func__);
+    return;
+  }
+  LOG_DEBUG("%s Started advertising", __func__);
 }
 
-void bluetooth::shim::Btm::StopAdvertising() {
-  bluetooth::shim::GetAdvertising()->StopAdvertising();
+void Btm::StopAdvertising() {
+  if (advertiser_id_ == hci::LeAdvertisingManager::kInvalidId) {
+    LOG_WARN("%s No active advertising", __func__);
+    return;
+  }
+  GetAdvertising()->RemoveAdvertiser(advertiser_id_);
+  advertiser_id_ = hci::LeAdvertisingManager::kInvalidId;
+  LOG_DEBUG("%s Stopped advertising", __func__);
 }
 
-void bluetooth::shim::Btm::StartConnectability() {
-  bluetooth::shim::GetAdvertising()->StartAdvertising();
+void Btm::StartConnectability() { StartAdvertising(); }
+
+void Btm::StopConnectability() { StopAdvertising(); }
+
+void Btm::StartActiveScanning() { StartScanning(kActiveScanning); }
+
+void Btm::StopActiveScanning() {
+  GetScanning()->StopScan(base::Bind([]() {}));
 }
 
-void bluetooth::shim::Btm::StopConnectability() {
-  bluetooth::shim::GetAdvertising()->StopAdvertising();
+void Btm::SetScanningTimer(uint64_t duration_ms,
+                           common::OnceCallback<void()> callback) {
+  scanning_timer_.Schedule(std::move(callback),
+                           std::chrono::milliseconds(duration_ms));
 }
 
-bool bluetooth::shim::Btm::StartActiveScanning() {
-  StartScanning(kActiveScanning);
+void Btm::CancelScanningTimer() { scanning_timer_.Cancel(); }
+
+void Btm::StartObserving() { StartScanning(kPassiveScanning); }
+
+void Btm::StopObserving() { StopActiveScanning(); }
+
+void Btm::SetObservingTimer(uint64_t duration_ms,
+                            common::OnceCallback<void()> callback) {
+  observing_timer_.Schedule(std::move(callback),
+                            std::chrono::milliseconds(duration_ms));
+}
+
+void Btm::CancelObservingTimer() { observing_timer_.Cancel(); }
+
+void Btm::StartScanning(bool use_active_scanning) {
+  GetScanning()->StartScan(&scanning_callbacks_);
+}
+
+size_t Btm::GetNumberOfAdvertisingInstances() const {
+  return GetAdvertising()->GetNumberOfAdvertisingInstances();
+}
+
+tBTM_STATUS Btm::CreateBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                            tBT_TRANSPORT transport, int device_type) {
+  if (transport == BTA_TRANSPORT_UNKNOWN) {
+    if (device_type & BT_DEVICE_TYPE_BLE) {
+      transport = BTA_TRANSPORT_LE;
+    } else if (device_type & BT_DEVICE_TYPE_BREDR) {
+      transport = BTA_TRANSPORT_BR_EDR;
+    }
+    LOG_DEBUG("%s guessing transport as %02x ", __func__, transport);
+  }
+
+  auto security_manager = GetSecurityModule()->GetSecurityManager();
+  switch (transport) {
+    case BT_TRANSPORT_BR_EDR:
+      security_manager->CreateBond(ToAddressWithType(bd_addr, BLE_ADDR_PUBLIC));
+      break;
+    case BT_TRANSPORT_LE:
+      security_manager->CreateBondLe(ToAddressWithType(bd_addr, addr_type));
+      break;
+    default:
+      return BTM_ILLEGAL_VALUE;
+  }
+  return BTM_SUCCESS;
+}
+
+bool Btm::CancelBond(const RawAddress& bd_addr) {
+  auto security_manager = GetSecurityModule()->GetSecurityManager();
+  security_manager->CancelBond(ToAddressWithType(bd_addr, BLE_ADDR_PUBLIC));
   return true;
 }
 
-bool bluetooth::shim::Btm::StopActiveScanning() {
-  bluetooth::shim::GetScanning()->StopScanning();
+bool Btm::RemoveBond(const RawAddress& bd_addr) {
+  // TODO(cmanton) Check if acl is connected
+  auto security_manager = GetSecurityModule()->GetSecurityManager();
+  security_manager->RemoveBond(ToAddressWithType(bd_addr, BLE_ADDR_PUBLIC));
   return true;
 }
 
-bool bluetooth::shim::Btm::StartObserving() {
-  StartScanning(kPassiveScanning);
-  return true;
+uint16_t Btm::GetAclHandle(const RawAddress& remote_bda,
+                           tBT_TRANSPORT transport) {
+  auto acl_manager = GetAclManager();
+  if (transport == BT_TRANSPORT_BR_EDR) {
+    return acl_manager->HACK_GetHandle(ToGdAddress(remote_bda));
+  } else {
+    return acl_manager->HACK_GetLeHandle(ToGdAddress(remote_bda));
+  }
 }
 
-bool bluetooth::shim::Btm::StopObserving() {
-  bluetooth::shim::GetScanning()->StopScanning();
-  return true;
-}
+}  // namespace shim
 
-void bluetooth::shim::Btm::StartScanning(bool use_active_scanning) {
-  bluetooth::shim::GetScanning()->StartScanning(
-      use_active_scanning,
-      [](AdvertisingReport report) {
-        LOG_INFO(LOG_TAG, "%s Received advertising report from device:%s",
-                 __func__, report.string_address.c_str());
-        RawAddress raw_address;
-        RawAddress::FromString(report.string_address, raw_address);
-
-        btm_ble_process_adv_addr(raw_address, &report.address_type);
-        btm_ble_process_adv_pkt_cont(
-            report.extended_event_type, report.address_type, raw_address,
-            kPhyConnectionLe1M, kPhyConnectionNone, kAdvDataInfoNotPresent,
-            kTxPowerInformationNotPresent, report.rssi,
-            kNotPeriodicAdvertisement, report.len, report.data);
-      },
-      [](DirectedAdvertisingReport report) {
-        LOG_WARN(LOG_TAG,
-                 "%s Directed advertising is unsupported from device:%s",
-                 __func__, report.string_address.c_str());
-      },
-      [](ExtendedAdvertisingReport report) {
-        LOG_INFO(LOG_TAG,
-                 "%s Received extended advertising report from device:%s",
-                 __func__, report.string_address.c_str());
-        RawAddress raw_address;
-        RawAddress::FromString(report.string_address, raw_address);
-        if (report.address_type != BLE_ADDR_ANONYMOUS) {
-          btm_ble_process_adv_addr(raw_address, &report.address_type);
-        }
-        btm_ble_process_adv_pkt_cont(
-            report.extended_event_type, report.address_type, raw_address,
-            kPhyConnectionLe1M, kPhyConnectionNone, kAdvDataInfoNotPresent,
-            kTxPowerInformationNotPresent, report.rssi,
-            kNotPeriodicAdvertisement, report.len, report.data);
-      },
-      []() { LOG_INFO(LOG_TAG, "%s Scanning timeout", __func__); });
-}
-
-size_t bluetooth::shim::Btm::GetNumberOfAdvertisingInstances() const {
-  return bluetooth::shim::GetAdvertising()->GetNumberOfAdvertisingInstances();
-}
+}  // namespace bluetooth
diff --git a/main/shim/btm.h b/main/shim/btm.h
index 6669fc3..77665ab 100644
--- a/main/shim/btm.h
+++ b/main/shim/btm.h
@@ -16,28 +16,41 @@
 
 #pragma once
 
+#include <cstddef>
 #include <cstdint>
+#include <functional>
 #include <mutex>
 #include <unordered_map>
 #include <vector>
 
+#include "hci/hci_packets.h"
+
 #include "stack/include/btm_api_types.h"
+#include "types/raw_address.h"
+
+#include "gd/common/callback.h"
+#include "gd/hci/le_advertising_manager.h"
+#include "gd/hci/le_scanning_manager.h"
+#include "gd/neighbor/inquiry.h"
+#include "gd/os/alarm.h"
+
+//
+// NOTE: limited and general constants for inquiry and discoverable are swapped
+//
 
 /* Discoverable modes */
-static constexpr int kDiscoverableModeOff = 0;
-static constexpr int kLimitedDiscoverableMode = 1;
-static constexpr int kGeneralDiscoverableMode = 2;
+static constexpr int kDiscoverableModeOff = 0;      // BTM_NON_DISCOVERABLE
+static constexpr int kLimitedDiscoverableMode = 1;  // BTM_LIMITED_DISCOVERABLE
+static constexpr int kGeneralDiscoverableMode = 2;  // BTM_GENERAL_DISCOVERABLE
 
 /* Inquiry modes */
-// NOTE: The inquiry general/limited are reversed from the discoverability
-// constants
-static constexpr int kInquiryModeOff = 0;
-static constexpr int kGeneralInquiryMode = 1;
-static constexpr int kLimitedInquiryMode = 2;
+static constexpr uint8_t kInquiryModeOff = 0;      // BTM_INQUIRY_NONE
+static constexpr uint8_t kGeneralInquiryMode = 1;  // BTM_GENERAL_INQUIRY
+static constexpr uint8_t kLimitedInquiryMode = 2;  // BTM_LIMITED_INQUIRY
 
 /* Connectable modes */
-static constexpr int kConnectibleModeOff = 0;
-static constexpr int kConnectibleModeOn = 1;
+static constexpr int kConnectibleModeOff = 0;  // BTM_NON_CONNECTABLE
+static constexpr int kConnectibleModeOn = 1;   // BTM_CONNECTABLE
 
 /* Inquiry and page scan modes */
 static constexpr int kStandardScanType = 0;
@@ -58,6 +71,9 @@
 static constexpr uint8_t kPhyConnectionLe2M = 0x02;
 static constexpr uint8_t kPhyConnectionLeCoded = 0x03;
 
+using LegacyInquiryCompleteCallback =
+    std::function<void(uint16_t status, uint8_t inquiry_mode)>;
+
 using DiscoverabilityState = struct {
   int mode;
   uint16_t interval;
@@ -69,71 +85,42 @@
 namespace shim {
 
 using BtmStatus = enum : uint16_t {
-  BTM_SUCCESS = 0,         /* 0  Command succeeded                 */
-  BTM_CMD_STARTED,         /* 1  Command started OK.               */
-  BTM_BUSY,                /* 2  Device busy with another command  */
-  BTM_NO_RESOURCES,        /* 3  No resources to issue command     */
-  BTM_MODE_UNSUPPORTED,    /* 4  Request for 1 or more unsupported modes */
-  BTM_ILLEGAL_VALUE,       /* 5  Illegal parameter value           */
-  BTM_WRONG_MODE,          /* 6  Device in wrong mode for request  */
-  BTM_UNKNOWN_ADDR,        /* 7  Unknown remote BD address         */
-  BTM_DEVICE_TIMEOUT,      /* 8  Device timeout                    */
-  BTM_BAD_VALUE_RET,       /* 9  A bad value was received from HCI */
-  BTM_ERR_PROCESSING,      /* 10 Generic error                     */
-  BTM_NOT_AUTHORIZED,      /* 11 Authorization failed              */
-  BTM_DEV_RESET,           /* 12 Device has been reset             */
-  BTM_CMD_STORED,          /* 13 request is stored in control block */
-  BTM_ILLEGAL_ACTION,      /* 14 state machine gets illegal command */
-  BTM_DELAY_CHECK,         /* 15 delay the check on encryption */
-  BTM_SCO_BAD_LENGTH,      /* 16 Bad SCO over HCI data length */
-  BTM_SUCCESS_NO_SECURITY, /* 17 security passed, no security set  */
-  BTM_FAILED_ON_SECURITY,  /* 18 security failed                   */
-  BTM_REPEATED_ATTEMPTS,   /* 19 repeated attempts for LE security requests */
-  BTM_MODE4_LEVEL4_NOT_SUPPORTED, /* 20 Secure Connections Only Mode can't be
+  BTM_SUCCESS = 0,              /* Command succeeded                 */
+  BTM_CMD_STARTED = 1,          /* Command started OK.               */
+  BTM_BUSY = 2,                 /* Device busy with another command  */
+  BTM_NO_RESOURCES = 3,         /* No resources to issue command     */
+  BTM_MODE_UNSUPPORTED = 4,     /* Request for 1 or more unsupported modes */
+  BTM_ILLEGAL_VALUE = 5,        /* Illegal parameter value           */
+  BTM_WRONG_MODE = 6,           /* Device in wrong mode for request  */
+  BTM_UNKNOWN_ADDR = 7,         /* Unknown remote BD address         */
+  BTM_DEVICE_TIMEOUT = 8,       /* Device timeout                    */
+  BTM_BAD_VALUE_RET = 9,        /* A bad value was received from HCI */
+  BTM_ERR_PROCESSING = 10,      /* Generic error                     */
+  BTM_NOT_AUTHORIZED = 11,      /* Authorization failed              */
+  BTM_DEV_RESET = 12,           /* Device has been reset             */
+  BTM_CMD_STORED = 13,          /* request is stored in control block */
+  BTM_ILLEGAL_ACTION = 14,      /* state machine gets illegal command */
+  BTM_DELAY_CHECK = 15,         /* delay the check on encryption */
+  BTM_SCO_BAD_LENGTH = 16,      /* Bad SCO over HCI data length */
+  BTM_SUCCESS_NO_SECURITY = 17, /* security passed, no security set  */
+  BTM_FAILED_ON_SECURITY = 18,  /* security failed                   */
+  BTM_REPEATED_ATTEMPTS = 19,   /* repeated attempts for LE security requests */
+  BTM_MODE4_LEVEL4_NOT_SUPPORTED = 20, /* Secure Connections Only Mode can't be
                                      supported */
-  BTM_DEV_BLACKLISTED             /* 21 The device is Blacklisted */
-};
-
-class ReadRemoteName {
- public:
-  bool Start(RawAddress raw_address) {
-    std::unique_lock<std::mutex> lock(mutex_);
-    if (in_progress_) {
-      return false;
-    }
-    raw_address_ = raw_address;
-    in_progress_ = true;
-    return true;
-  }
-
-  void Stop() {
-    std::unique_lock<std::mutex> lock(mutex_);
-    raw_address_ = RawAddress::kEmpty;
-    in_progress_ = false;
-  }
-
-  bool IsInProgress() const { return in_progress_; }
-
-  std::string AddressString() const { return raw_address_.ToString(); }
-
-  ReadRemoteName() : in_progress_{false}, raw_address_(RawAddress::kEmpty) {}
-
- private:
-  bool in_progress_;
-  RawAddress raw_address_;
-  std::mutex mutex_;
+  BTM_DEV_BLACKLISTED = 21,            /* The device is Blacklisted */
 };
 
 class Btm {
  public:
-  Btm();
-  ~Btm();
+  // |handler| is used to run timer tasks and scan callbacks
+  Btm(os::Handler* handler, neighbor::InquiryModule* inquiry);
+  ~Btm() = default;
 
-  // Callbacks
-  void OnInquiryResult(std::vector<const uint8_t> result);
-  void OnInquiryResultWithRssi(std::vector<const uint8_t> result);
-  void OnExtendedInquiryResult(std::vector<const uint8_t> result);
-  void OnInquiryComplete(uint16_t status);
+  // Inquiry result callbacks
+  void OnInquiryResult(bluetooth::hci::InquiryResultView view);
+  void OnInquiryResultWithRssi(bluetooth::hci::InquiryResultWithRssiView view);
+  void OnExtendedInquiryResult(bluetooth::hci::ExtendedInquiryResultView view);
+  void OnInquiryComplete(bluetooth::hci::ErrorCode status);
 
   // Inquiry API
   bool SetInquiryFilter(uint8_t mode, uint8_t type, tBTM_INQ_FILT_COND data);
@@ -141,15 +128,16 @@
   void SetFilterInquiryOnDevice();
   void ClearInquiryFilter();
 
-  bool SetStandardInquiryResultMode();
-  bool SetInquiryWithRssiResultMode();
-  bool SetExtendedInquiryResultMode();
+  void SetStandardInquiryResultMode();
+  void SetInquiryWithRssiResultMode();
+  void SetExtendedInquiryResultMode();
 
   void SetInterlacedInquiryScan();
   void SetStandardInquiryScan();
   bool IsInterlacedScanSupported() const;
 
-  bool StartInquiry(uint8_t mode, uint8_t duration, uint8_t max_responses);
+  bool StartInquiry(uint8_t mode, uint8_t duration, uint8_t max_responses,
+                    LegacyInquiryCompleteCallback inquiry_complete_callback);
   void CancelInquiry();
   bool IsInquiryActive() const;
   bool IsGeneralInquiryActive() const;
@@ -163,6 +151,11 @@
   bool IsGeneralPeriodicInquiryActive() const;
   bool IsLimitedPeriodicInquiryActive() const;
 
+  // Discoverability API
+  bool general_inquiry_active_{false};
+  bool limited_inquiry_active_{false};
+  bool general_periodic_inquiry_active_{false};
+  bool limited_periodic_inquiry_active_{false};
   void SetClassicGeneralDiscoverability(uint16_t window, uint16_t interval);
   void SetClassicLimitedDiscoverability(uint16_t window, uint16_t interval);
   void SetClassicDiscoverabilityOff();
@@ -185,29 +178,74 @@
 
   bool IsLeAclConnected(const RawAddress& raw_address) const;
 
-  // Remote device name
+  // Remote device name API
   BtmStatus ReadClassicRemoteDeviceName(const RawAddress& raw_address,
                                         tBTM_CMPL_CB* callback);
   BtmStatus ReadLeRemoteDeviceName(const RawAddress& raw_address,
                                    tBTM_CMPL_CB* callback);
   BtmStatus CancelAllReadRemoteDeviceName();
 
+  // Le neighbor interaction API
+  bluetooth::hci::AdvertiserId advertiser_id_{
+      hci::LeAdvertisingManager::kInvalidId};
   void StartAdvertising();
   void StopAdvertising();
   void StartConnectability();
   void StopConnectability();
 
-  bool StartActiveScanning();
-  bool StopActiveScanning();
+  void StartActiveScanning();
+  void StopActiveScanning();
 
-  bool StartObserving();
-  bool StopObserving();
+  void StartObserving();
+  void StopObserving();
 
   size_t GetNumberOfAdvertisingInstances() const;
 
+  void SetObservingTimer(uint64_t duration_ms,
+                         common::OnceCallback<void()> callback);
+  void CancelObservingTimer();
+  void SetScanningTimer(uint64_t duration_ms,
+                        common::OnceCallback<void()> callback);
+  void CancelScanningTimer();
+
+  tBTM_STATUS CreateBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                         tBT_TRANSPORT transport, int device_type);
+  bool CancelBond(const RawAddress& bd_addr);
+  bool RemoveBond(const RawAddress& bd_addr);
+
+  uint16_t GetAclHandle(const RawAddress& remote_bda, tBT_TRANSPORT transport);
+
  private:
+  os::Alarm scanning_timer_;
+  os::Alarm observing_timer_;
+
+  LegacyInquiryCompleteCallback legacy_inquiry_complete_callback_{};
+  uint8_t active_inquiry_mode_ = 0;
+
+  class ReadRemoteName {
+   public:
+    ReadRemoteName() = default;
+    bool Start(RawAddress raw_address);
+    void Stop();
+    bool IsInProgress() const;
+    std::string AddressString() const;
+
+   private:
+    std::mutex mutex_;
+    bool in_progress_ = false;
+    RawAddress raw_address_ = RawAddress::kEmpty;
+  };
   ReadRemoteName le_read_remote_name_;
   ReadRemoteName classic_read_remote_name_;
+
+  class ScanningCallbacks : public hci::LeScanningManagerCallbacks {
+    void on_advertisements(
+        std::vector<std::shared_ptr<hci::LeReport>> reports) override;
+    void on_timeout() override;
+    os::Handler* Handler() override;
+  };
+  ScanningCallbacks scanning_callbacks_;
+
   // TODO(cmanton) abort if there is no classic acl link up
   bool CheckClassicAclLink(const RawAddress& raw_address) { return true; }
   bool CheckLeAclLink(const RawAddress& raw_address) { return true; }
diff --git a/main/shim/btm_api.cc b/main/shim/btm_api.cc
index 79d98ef..ad35dc3 100644
--- a/main/shim/btm_api.cc
+++ b/main/shim/btm_api.cc
@@ -18,22 +18,262 @@
 
 #include <base/callback.h>
 
+#include <mutex>
+
+#include "common/time_util.h"
+#include "device/include/controller.h"
 #include "main/shim/btm.h"
 #include "main/shim/btm_api.h"
-#include "osi/include/log.h"
+#include "main/shim/controller.h"
+#include "main/shim/shim.h"
+#include "main/shim/stack.h"
 #include "stack/btm/btm_int_types.h"
-
-static bluetooth::shim::Btm shim_btm;
+#include "types/raw_address.h"
 
 /**
  * Legacy bluetooth module global control block state
+ *
+ * Mutex is used to synchronize access from the shim
+ * layer into the global control block.  This is used
+ * by the shim despite potentially arbitrary
+ * unsynchronized access by the legacy stack.
  */
 extern tBTM_CB btm_cb;
+std::mutex btm_cb_mutex_;
 
+extern bool btm_inq_find_bdaddr(const RawAddress& p_bda);
+extern tINQ_DB_ENT* btm_inq_db_find(const RawAddress& raw_address);
+extern tINQ_DB_ENT* btm_inq_db_new(const RawAddress& p_bda);
+
+/**
+ * Legacy bluetooth btm stack entry points
+ */
 extern void btm_acl_update_busy_level(tBTM_BLI_EVENT event);
 extern void btm_clear_all_pending_le_entry(void);
 extern void btm_clr_inq_result_flt(void);
+extern void btm_set_eir_uuid(uint8_t* p_eir, tBTM_INQ_RESULTS* p_results);
 extern void btm_sort_inq_result(void);
+extern void btm_process_inq_complete(uint8_t status, uint8_t result_type);
+
+static bool max_responses_reached() {
+  return (btm_cb.btm_inq_vars.inqparms.max_resps &&
+          btm_cb.btm_inq_vars.inq_cmpl_info.num_resp >=
+              btm_cb.btm_inq_vars.inqparms.max_resps);
+}
+
+static bool is_periodic_inquiry_active() {
+  return btm_cb.btm_inq_vars.inq_active & BTM_PERIODIC_INQUIRY_ACTIVE;
+}
+
+static bool has_le_device(tBT_DEVICE_TYPE device_type) {
+  return device_type & BT_DEVICE_TYPE_BLE;
+}
+
+static bool is_classic_device(tBT_DEVICE_TYPE device_type) {
+  return device_type == BT_DEVICE_TYPE_BREDR;
+}
+
+static bool has_classic_device(tBT_DEVICE_TYPE device_type) {
+  return device_type & BT_DEVICE_TYPE_BREDR;
+}
+
+static bool is_dual_mode_device(tBT_DEVICE_TYPE device_type) {
+  return device_type == BT_DEVICE_TYPE_DUMO;
+}
+
+static bool is_observing_or_active_scanning() {
+  return btm_cb.btm_inq_vars.inqparms.mode & BTM_BLE_INQUIRY_MASK;
+}
+
+static void check_exceeded_responses(tBT_DEVICE_TYPE device_type,
+                                     bool scan_rsp) {
+  if (!is_periodic_inquiry_active() && max_responses_reached() &&
+      ((is_observing_or_active_scanning() && is_dual_mode_device(device_type) &&
+        scan_rsp) ||
+       (is_observing_or_active_scanning()))) {
+    LOG_INFO("UNIMPLEMENTED %s Device max responses found...cancelling inquiry",
+             __func__);
+  }
+}
+
+void btm_api_process_inquiry_result(const RawAddress& raw_address,
+                                    uint8_t page_scan_rep_mode,
+                                    DEV_CLASS device_class,
+                                    uint16_t clock_offset) {
+  tINQ_DB_ENT* p_i = btm_inq_db_find(raw_address);
+  if (max_responses_reached()) {
+    if (p_i == nullptr || !has_le_device(p_i->inq_info.results.device_type)) {
+      return;
+    }
+  }
+
+  if (p_i == nullptr) {
+    p_i = btm_inq_db_new(raw_address);
+    CHECK(p_i != nullptr);
+  } else if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
+             is_classic_device(p_i->inq_info.results.device_type)) {
+    return;
+  }
+
+  p_i->inq_info.results.page_scan_rep_mode = page_scan_rep_mode;
+  p_i->inq_info.results.page_scan_per_mode = 0;  // RESERVED
+  p_i->inq_info.results.page_scan_mode = 0;      // RESERVED
+  p_i->inq_info.results.dev_class[0] = device_class[0];
+  p_i->inq_info.results.dev_class[1] = device_class[1];
+  p_i->inq_info.results.dev_class[2] = device_class[2];
+  p_i->inq_info.results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;
+  p_i->inq_info.results.inq_result_type = BTM_INQ_RESULT_BR;
+  p_i->inq_info.results.rssi = BTM_INQ_RES_IGNORE_RSSI;
+
+  p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
+  p_i->inq_count = btm_cb.btm_inq_vars.inq_counter;
+  p_i->inq_info.appl_knows_rem_name = false;
+
+  if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
+    p_i->inq_info.results.device_type = BT_DEVICE_TYPE_BREDR;
+    btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
+    p_i->scan_rsp = false;
+  } else {
+    p_i->inq_info.results.device_type |= BT_DEVICE_TYPE_BREDR;
+  }
+
+  check_exceeded_responses(p_i->inq_info.results.device_type, p_i->scan_rsp);
+  if (btm_cb.btm_inq_vars.p_inq_results_cb == nullptr) {
+    return;
+  }
+
+  (btm_cb.btm_inq_vars.p_inq_results_cb)(&p_i->inq_info.results, nullptr, 0);
+}
+
+void btm_api_process_inquiry_result_with_rssi(RawAddress raw_address,
+                                              uint8_t page_scan_rep_mode,
+                                              DEV_CLASS device_class,
+                                              uint16_t clock_offset,
+                                              int8_t rssi) {
+  tINQ_DB_ENT* p_i = btm_inq_db_find(raw_address);
+  if (max_responses_reached()) {
+    if (p_i == nullptr || !has_le_device(p_i->inq_info.results.device_type)) {
+      return;
+    }
+  }
+
+  bool update = false;
+  if (btm_inq_find_bdaddr(raw_address)) {
+    if (btm_cb.btm_inq_vars.inqparms.report_dup && p_i != nullptr &&
+        (rssi > p_i->inq_info.results.rssi || p_i->inq_info.results.rssi == 0 ||
+         has_classic_device(p_i->inq_info.results.device_type))) {
+      update = true;
+    }
+  }
+
+  bool is_new = true;
+  if (p_i == nullptr) {
+    p_i = btm_inq_db_new(raw_address);
+    CHECK(p_i != nullptr);
+  } else if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
+             is_classic_device(p_i->inq_info.results.device_type)) {
+    is_new = false;
+  }
+
+  p_i->inq_info.results.rssi = rssi;
+
+  if (is_new) {
+    p_i->inq_info.results.page_scan_rep_mode = page_scan_rep_mode;
+    p_i->inq_info.results.page_scan_per_mode = 0;  // RESERVED
+    p_i->inq_info.results.page_scan_mode = 0;      // RESERVED
+    p_i->inq_info.results.dev_class[0] = device_class[0];
+    p_i->inq_info.results.dev_class[1] = device_class[1];
+    p_i->inq_info.results.dev_class[2] = device_class[2];
+    p_i->inq_info.results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;
+    p_i->inq_info.results.inq_result_type = BTM_INQ_RESULT_BR;
+
+    p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
+    p_i->inq_count = btm_cb.btm_inq_vars.inq_counter;
+    p_i->inq_info.appl_knows_rem_name = false;
+
+    if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
+      p_i->inq_info.results.device_type = BT_DEVICE_TYPE_BREDR;
+      btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
+      p_i->scan_rsp = false;
+    } else {
+      p_i->inq_info.results.device_type |= BT_DEVICE_TYPE_BREDR;
+    }
+  }
+
+  check_exceeded_responses(p_i->inq_info.results.device_type, p_i->scan_rsp);
+  if (btm_cb.btm_inq_vars.p_inq_results_cb == nullptr) {
+    return;
+  }
+
+  if (is_new || update) {
+    (btm_cb.btm_inq_vars.p_inq_results_cb)(&p_i->inq_info.results, nullptr, 0);
+  }
+}
+void btm_api_process_extended_inquiry_result(RawAddress raw_address,
+                                             uint8_t page_scan_rep_mode,
+                                             DEV_CLASS device_class,
+                                             uint16_t clock_offset, int8_t rssi,
+                                             const uint8_t* eir_data,
+                                             size_t eir_len) {
+  tINQ_DB_ENT* p_i = btm_inq_db_find(raw_address);
+  if (max_responses_reached()) {
+    if (p_i == nullptr || !has_le_device(p_i->inq_info.results.device_type)) {
+      return;
+    }
+  }
+
+  bool update = false;
+  if (btm_inq_find_bdaddr(raw_address) && p_i != nullptr) {
+    update = true;
+  }
+
+  bool is_new = true;
+  if (p_i == nullptr) {
+    p_i = btm_inq_db_new(raw_address);
+  } else if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
+             (p_i->inq_info.results.device_type == BT_DEVICE_TYPE_BREDR)) {
+    is_new = false;
+  }
+
+  p_i->inq_info.results.rssi = rssi;
+
+  if (is_new) {
+    p_i->inq_info.results.page_scan_rep_mode = page_scan_rep_mode;
+    p_i->inq_info.results.page_scan_per_mode = 0;  // RESERVED
+    p_i->inq_info.results.page_scan_mode = 0;      // RESERVED
+    p_i->inq_info.results.dev_class[0] = device_class[0];
+    p_i->inq_info.results.dev_class[1] = device_class[1];
+    p_i->inq_info.results.dev_class[2] = device_class[2];
+    p_i->inq_info.results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;
+    p_i->inq_info.results.inq_result_type = BTM_INQ_RESULT_BR;
+
+    p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
+    p_i->inq_count = btm_cb.btm_inq_vars.inq_counter;
+    p_i->inq_info.appl_knows_rem_name = false;
+
+    if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
+      p_i->inq_info.results.device_type = BT_DEVICE_TYPE_BREDR;
+      btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
+      p_i->scan_rsp = false;
+    } else {
+      p_i->inq_info.results.device_type |= BT_DEVICE_TYPE_BREDR;
+    }
+  }
+
+  check_exceeded_responses(p_i->inq_info.results.device_type, p_i->scan_rsp);
+  if (btm_cb.btm_inq_vars.p_inq_results_cb == nullptr) {
+    return;
+  }
+
+  if (is_new || update) {
+    memset(p_i->inq_info.results.eir_uuid, 0,
+           BTM_EIR_SERVICE_ARRAY_SIZE * (BTM_EIR_ARRAY_BITS / 8));
+    btm_set_eir_uuid(const_cast<uint8_t*>(eir_data), &p_i->inq_info.results);
+    uint8_t* p_eir_data = const_cast<uint8_t*>(eir_data);
+    (btm_cb.btm_inq_vars.p_inq_results_cb)(&p_i->inq_info.results, p_eir_data,
+                                           eir_len);
+  }
+}
 
 tBTM_STATUS bluetooth::shim::BTM_StartInquiry(tBTM_INQ_PARMS* p_inqparms,
                                               tBTM_INQ_RESULTS_CB* p_results_cb,
@@ -42,24 +282,73 @@
   CHECK(p_results_cb != nullptr);
   CHECK(p_cmpl_cb != nullptr);
 
-  btm_cb.btm_inq_vars.inq_cmpl_info.num_resp =
-      0; /* Clear the results counter */
+  std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+
+  btm_cb.btm_inq_vars.inq_cmpl_info.num_resp = 0;
+  btm_cb.btm_inq_vars.scan_type = INQ_GENERAL;
+
+  Stack::GetInstance()->GetBtm()->StartActiveScanning();
+  if (p_inqparms->duration != 0) {
+    Stack::GetInstance()->GetBtm()->SetScanningTimer(
+        p_inqparms->duration * 1000, common::BindOnce([]() {
+          LOG_INFO("%s scanning timeout popped", __func__);
+          std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+          Stack::GetInstance()->GetBtm()->StopActiveScanning();
+        }));
+  }
+
+  Stack::GetInstance()->GetBtm()->StartActiveScanning();
 
   uint8_t classic_mode = p_inqparms->mode & 0x0f;
-
-  if (!shim_btm.StartActiveScanning()) {
-    LOG_WARN(LOG_TAG, "%s Unable to start le active scanning", __func__);
-    return BTM_ERR_PROCESSING;
-  }
-  if (!shim_btm.SetInquiryFilter(classic_mode, p_inqparms->filter_cond_type,
-                                 p_inqparms->filter_cond)) {
-    LOG_WARN(LOG_TAG, "%s Unable to set inquiry filter", __func__);
+  if (!Stack::GetInstance()->GetBtm()->SetInquiryFilter(
+          classic_mode, p_inqparms->filter_cond_type,
+          p_inqparms->filter_cond)) {
+    LOG_WARN("%s Unable to set inquiry filter", __func__);
     return BTM_ERR_PROCESSING;
   }
 
-  if (!shim_btm.StartInquiry(classic_mode, p_inqparms->duration,
-                             p_inqparms->max_resps)) {
-    LOG_WARN(LOG_TAG, "%s Unable to start inquiry", __func__);
+  if (!Stack::GetInstance()->GetBtm()->StartInquiry(
+          classic_mode, p_inqparms->duration, p_inqparms->max_resps,
+          [](uint16_t status, uint8_t inquiry_mode) {
+            LOG_DEBUG("%s Inquiry is complete status:%hd inquiry_mode:%hhd",
+                      __func__, status, inquiry_mode);
+            btm_cb.btm_inq_vars.inqparms.mode &= ~(inquiry_mode);
+
+            btm_acl_update_busy_level(BTM_BLI_INQ_DONE_EVT);
+            if (btm_cb.btm_inq_vars.inq_active) {
+              btm_cb.btm_inq_vars.inq_cmpl_info.status = status;
+              btm_clear_all_pending_le_entry();
+              btm_cb.btm_inq_vars.state = BTM_INQ_INACTIVE_STATE;
+
+              /* Increment so the start of a next inquiry has a new count */
+              btm_cb.btm_inq_vars.inq_counter++;
+
+              btm_clr_inq_result_flt();
+
+              if ((status == BTM_SUCCESS) &&
+                  controller_get_interface()
+                      ->supports_rssi_with_inquiry_results()) {
+                btm_sort_inq_result();
+              }
+
+              btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE;
+              btm_cb.btm_inq_vars.p_inq_results_cb = nullptr;
+              btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+
+              if (btm_cb.btm_inq_vars.p_inq_cmpl_cb != nullptr) {
+                LOG_DEBUG("%s Sending inquiry completion to upper layer",
+                          __func__);
+                (btm_cb.btm_inq_vars.p_inq_cmpl_cb)(
+                    (tBTM_INQUIRY_CMPL*)&btm_cb.btm_inq_vars.inq_cmpl_info);
+                btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+              }
+            }
+            if (btm_cb.btm_inq_vars.inqparms.mode == BTM_INQUIRY_NONE &&
+                btm_cb.btm_inq_vars.scan_type == INQ_GENERAL) {
+              btm_cb.btm_inq_vars.scan_type = INQ_NONE;
+            }
+          })) {
+    LOG_WARN("%s Unable to start inquiry", __func__);
     return BTM_ERR_PROCESSING;
   }
 
@@ -73,44 +362,6 @@
   return BTM_CMD_STARTED;
 }
 
-tBTM_STATUS bluetooth::shim::BTM_SetPeriodicInquiryMode(
-    tBTM_INQ_PARMS* p_inqparms, uint16_t max_delay, uint16_t min_delay,
-    tBTM_INQ_RESULTS_CB* p_results_cb) {
-  CHECK(p_inqparms != nullptr);
-  CHECK(p_results_cb != nullptr);
-
-  if (p_inqparms->duration < BTM_MIN_INQUIRY_LEN ||
-      p_inqparms->duration > BTM_MAX_INQUIRY_LENGTH ||
-      min_delay <= p_inqparms->duration ||
-      min_delay < BTM_PER_INQ_MIN_MIN_PERIOD ||
-      min_delay > BTM_PER_INQ_MAX_MIN_PERIOD || max_delay <= min_delay ||
-      max_delay < BTM_PER_INQ_MIN_MAX_PERIOD) {
-    return (BTM_ILLEGAL_VALUE);
-  }
-
-  if (shim_btm.IsInquiryActive()) {
-    return BTM_BUSY;
-  }
-
-  switch (p_inqparms->filter_cond_type) {
-    case kClearInquiryFilter:
-      shim_btm.ClearInquiryFilter();
-      return BTM_SUCCESS;
-      break;
-    case kFilterOnDeviceClass:
-      shim_btm.SetFilterInquiryOnDevice();
-      return BTM_SUCCESS;
-      break;
-    case kFilterOnAddress:
-      shim_btm.SetFilterInquiryOnAddress();
-      return BTM_SUCCESS;
-      break;
-    default:
-      return BTM_ILLEGAL_VALUE;
-  }
-  return BTM_MODE_UNSUPPORTED;
-}
-
 tBTM_STATUS bluetooth::shim::BTM_SetDiscoverability(uint16_t discoverable_mode,
                                                     uint16_t window,
                                                     uint16_t interval) {
@@ -122,30 +373,32 @@
 
   switch (le_discoverable_mode) {
     case kDiscoverableModeOff:
-      shim_btm.StopAdvertising();
+      Stack::GetInstance()->GetBtm()->StopAdvertising();
       break;
     case kLimitedDiscoverableMode:
     case kGeneralDiscoverableMode:
-      shim_btm.StartAdvertising();
+      Stack::GetInstance()->GetBtm()->StartAdvertising();
       break;
     default:
-      LOG_WARN(LOG_TAG, "%s Unexpected le discoverability mode:%d", __func__,
+      LOG_WARN("%s Unexpected le discoverability mode:%d", __func__,
                le_discoverable_mode);
   }
 
   switch (classic_discoverable_mode) {
     case kDiscoverableModeOff:
-      shim_btm.SetClassicDiscoverabilityOff();
+      Stack::GetInstance()->GetBtm()->SetClassicDiscoverabilityOff();
       break;
     case kLimitedDiscoverableMode:
-      shim_btm.SetClassicLimitedDiscoverability(window, interval);
+      Stack::GetInstance()->GetBtm()->SetClassicLimitedDiscoverability(
+          window, interval);
       break;
     case kGeneralDiscoverableMode:
-      shim_btm.SetClassicGeneralDiscoverability(window, interval);
+      Stack::GetInstance()->GetBtm()->SetClassicGeneralDiscoverability(
+          window, interval);
       break;
     default:
-      LOG_WARN(LOG_TAG, "%s Unexpected classic discoverability mode:%d",
-               __func__, classic_discoverable_mode);
+      LOG_WARN("%s Unexpected classic discoverability mode:%d", __func__,
+               classic_discoverable_mode);
   }
   return BTM_SUCCESS;
 }
@@ -153,11 +406,11 @@
 tBTM_STATUS bluetooth::shim::BTM_SetInquiryScanType(uint16_t scan_type) {
   switch (scan_type) {
     case kInterlacedScanType:
-      shim_btm.SetInterlacedInquiryScan();
+      Stack::GetInstance()->GetBtm()->SetInterlacedInquiryScan();
       return BTM_SUCCESS;
       break;
     case kStandardScanType:
-      shim_btm.SetStandardInquiryScan();
+      Stack::GetInstance()->GetBtm()->SetStandardInquiryScan();
       return BTM_SUCCESS;
       break;
     default:
@@ -173,27 +426,69 @@
     CHECK(p_results_cb != nullptr);
     CHECK(p_cmpl_cb != nullptr);
 
+    std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+
     if (btm_cb.ble_ctr_cb.scan_activity & BTM_LE_OBSERVE_ACTIVE) {
-      LOG_WARN(LOG_TAG, "%s Observing already active", __func__);
+      LOG_WARN("%s Observing already active", __func__);
       return BTM_WRONG_MODE;
     }
 
     btm_cb.ble_ctr_cb.p_obs_results_cb = p_results_cb;
     btm_cb.ble_ctr_cb.p_obs_cmpl_cb = p_cmpl_cb;
-    if (!shim_btm.StartObserving()) {
-      LOG_WARN(LOG_TAG, "%s Unable to start le observing", __func__);
-      return BTM_ERR_PROCESSING;
-    }
+    Stack::GetInstance()->GetBtm()->StartObserving();
     btm_cb.ble_ctr_cb.scan_activity |= BTM_LE_OBSERVE_ACTIVE;
+
+    if (duration_sec != 0) {
+      Stack::GetInstance()->GetBtm()->SetObservingTimer(
+          duration_sec * 1000, common::BindOnce([]() {
+            LOG_DEBUG("%s observing timeout popped", __func__);
+
+            Stack::GetInstance()->GetBtm()->CancelObservingTimer();
+            Stack::GetInstance()->GetBtm()->StopObserving();
+
+            std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+            btm_cb.ble_ctr_cb.scan_activity &= ~BTM_LE_OBSERVE_ACTIVE;
+
+            if (btm_cb.ble_ctr_cb.p_obs_cmpl_cb) {
+              (btm_cb.ble_ctr_cb.p_obs_cmpl_cb)(
+                  &btm_cb.btm_inq_vars.inq_cmpl_info);
+            }
+            btm_cb.ble_ctr_cb.p_obs_results_cb = nullptr;
+            btm_cb.ble_ctr_cb.p_obs_cmpl_cb = nullptr;
+
+            btm_cb.btm_inq_vars.inqparms.mode &= ~(BTM_BLE_INQUIRY_MASK);
+            btm_cb.btm_inq_vars.scan_type = INQ_NONE;
+
+            btm_acl_update_busy_level(BTM_BLI_INQ_DONE_EVT);
+
+            btm_clear_all_pending_le_entry();
+            btm_cb.btm_inq_vars.state = BTM_INQ_INACTIVE_STATE;
+
+            btm_cb.btm_inq_vars.inq_counter++;
+            btm_clr_inq_result_flt();
+            btm_sort_inq_result();
+
+            btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE;
+            btm_cb.btm_inq_vars.p_inq_results_cb = NULL;
+            btm_cb.btm_inq_vars.p_inq_cmpl_cb = NULL;
+
+            if (btm_cb.btm_inq_vars.p_inq_cmpl_cb) {
+              (btm_cb.btm_inq_vars.p_inq_cmpl_cb)(
+                  (tBTM_INQUIRY_CMPL*)&btm_cb.btm_inq_vars.inq_cmpl_info);
+              btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+            }
+          }));
+    }
   } else {
+    std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+
     if (!(btm_cb.ble_ctr_cb.scan_activity & BTM_LE_OBSERVE_ACTIVE)) {
-      LOG_WARN(LOG_TAG, "%s Observing already inactive", __func__);
+      LOG_WARN("%s Observing already inactive", __func__);
     }
+    Stack::GetInstance()->GetBtm()->CancelObservingTimer();
+    Stack::GetInstance()->GetBtm()->StopObserving();
     btm_cb.ble_ctr_cb.scan_activity &= ~BTM_LE_OBSERVE_ACTIVE;
-    if (!shim_btm.StopObserving()) {
-      LOG_WARN(LOG_TAG, "%s Unable to stop le observing", __func__);
-      return BTM_ERR_PROCESSING;
-    }
+    Stack::GetInstance()->GetBtm()->StopObserving();
     if (btm_cb.ble_ctr_cb.p_obs_cmpl_cb) {
       (btm_cb.ble_ctr_cb.p_obs_cmpl_cb)(&btm_cb.btm_inq_vars.inq_cmpl_info);
     }
@@ -206,14 +501,14 @@
 tBTM_STATUS bluetooth::shim::BTM_SetPageScanType(uint16_t scan_type) {
   switch (scan_type) {
     case kInterlacedScanType:
-      if (!shim_btm.IsInterlacedScanSupported()) {
+      if (!Stack::GetInstance()->GetBtm()->IsInterlacedScanSupported()) {
         return BTM_MODE_UNSUPPORTED;
       }
-      shim_btm.SetInterlacedPageScan();
+      Stack::GetInstance()->GetBtm()->SetInterlacedPageScan();
       return BTM_SUCCESS;
       break;
     case kStandardScanType:
-      shim_btm.SetStandardPageScan();
+      Stack::GetInstance()->GetBtm()->SetStandardPageScan();
       return BTM_SUCCESS;
       break;
     default:
@@ -225,29 +520,24 @@
 tBTM_STATUS bluetooth::shim::BTM_SetInquiryMode(uint8_t inquiry_mode) {
   switch (inquiry_mode) {
     case kStandardInquiryResult:
-      if (shim_btm.SetStandardInquiryResultMode()) {
-        return BTM_SUCCESS;
-      }
+      Stack::GetInstance()->GetBtm()->SetStandardInquiryResultMode();
       break;
     case kInquiryResultWithRssi:
-      if (shim_btm.SetInquiryWithRssiResultMode()) {
-        return BTM_SUCCESS;
-      }
+      Stack::GetInstance()->GetBtm()->SetInquiryWithRssiResultMode();
       break;
     case kExtendedInquiryResult:
-      if (shim_btm.SetExtendedInquiryResultMode()) {
-        return BTM_SUCCESS;
-      }
+      Stack::GetInstance()->GetBtm()->SetExtendedInquiryResultMode();
       break;
     default:
       return BTM_ILLEGAL_VALUE;
   }
-  return BTM_MODE_UNSUPPORTED;
+  return BTM_SUCCESS;
 }
 
 uint16_t bluetooth::shim::BTM_ReadDiscoverability(uint16_t* p_window,
                                                   uint16_t* p_interval) {
-  DiscoverabilityState state = shim_btm.GetClassicDiscoverabilityState();
+  DiscoverabilityState state =
+      Stack::GetInstance()->GetBtm()->GetClassicDiscoverabilityState();
 
   if (p_interval) *p_interval = state.interval;
   if (p_window) *p_window = state.window;
@@ -256,7 +546,7 @@
 }
 
 tBTM_STATUS bluetooth::shim::BTM_CancelPeriodicInquiry(void) {
-  shim_btm.CancelPeriodicInquiry();
+  Stack::GetInstance()->GetBtm()->CancelPeriodicInquiry();
   return BTM_SUCCESS;
 }
 
@@ -271,10 +561,10 @@
 
   switch (le_connectible_mode) {
     case kConnectibleModeOff:
-      shim_btm.StopConnectability();
+      Stack::GetInstance()->GetBtm()->StopConnectability();
       break;
     case kConnectibleModeOn:
-      shim_btm.StartConnectability();
+      Stack::GetInstance()->GetBtm()->StartConnectability();
       break;
     default:
       return BTM_ILLEGAL_VALUE;
@@ -283,10 +573,10 @@
 
   switch (classic_connectible_mode) {
     case kConnectibleModeOff:
-      shim_btm.SetClassicConnectibleOff();
+      Stack::GetInstance()->GetBtm()->SetClassicConnectibleOff();
       break;
     case kConnectibleModeOn:
-      shim_btm.SetClassicConnectibleOn();
+      Stack::GetInstance()->GetBtm()->SetClassicConnectibleOn();
       break;
     default:
       return BTM_ILLEGAL_VALUE;
@@ -297,7 +587,8 @@
 
 uint16_t bluetooth::shim::BTM_ReadConnectability(uint16_t* p_window,
                                                  uint16_t* p_interval) {
-  ConnectabilityState state = shim_btm.GetClassicConnectabilityState();
+  ConnectabilityState state =
+      Stack::GetInstance()->GetBtm()->GetClassicConnectabilityState();
 
   if (p_window) *p_window = state.window;
   if (p_interval) *p_interval = state.interval;
@@ -306,19 +597,59 @@
 }
 
 uint16_t bluetooth::shim::BTM_IsInquiryActive(void) {
-  if (shim_btm.IsLimitedInquiryActive()) {
+  if (Stack::GetInstance()->GetBtm()->IsLimitedInquiryActive()) {
     return BTM_LIMITED_INQUIRY_ACTIVE;
-  } else if (shim_btm.IsGeneralInquiryActive()) {
+  } else if (Stack::GetInstance()->GetBtm()->IsGeneralInquiryActive()) {
     return BTM_GENERAL_INQUIRY_ACTIVE;
-  } else if (shim_btm.IsGeneralPeriodicInquiryActive() ||
-             shim_btm.IsLimitedPeriodicInquiryActive()) {
+  } else if (Stack::GetInstance()->GetBtm()->IsGeneralPeriodicInquiryActive() ||
+             Stack::GetInstance()->GetBtm()->IsLimitedPeriodicInquiryActive()) {
     return BTM_PERIODIC_INQUIRY_ACTIVE;
   }
   return BTM_INQUIRY_INACTIVE;
 }
 
 tBTM_STATUS bluetooth::shim::BTM_CancelInquiry(void) {
-  shim_btm.CancelInquiry();
+  LOG_DEBUG("%s Cancel inquiry", __func__);
+  Stack::GetInstance()->GetBtm()->CancelInquiry();
+
+  btm_cb.btm_inq_vars.state = BTM_INQ_INACTIVE_STATE;
+  btm_clr_inq_result_flt();
+
+  Stack::GetInstance()->GetBtm()->CancelScanningTimer();
+  Stack::GetInstance()->GetBtm()->StopActiveScanning();
+
+  btm_cb.ble_ctr_cb.scan_activity &= ~BTM_BLE_INQUIRY_MASK;
+
+  btm_cb.btm_inq_vars.inqparms.mode &=
+      ~(btm_cb.btm_inq_vars.inqparms.mode & BTM_BLE_INQUIRY_MASK);
+
+  btm_acl_update_busy_level(BTM_BLI_INQ_DONE_EVT);
+  /* Ignore any stray or late complete messages if the inquiry is not active */
+  if (btm_cb.btm_inq_vars.inq_active) {
+    btm_cb.btm_inq_vars.inq_cmpl_info.status = BTM_SUCCESS;
+    btm_clear_all_pending_le_entry();
+
+    if (controller_get_interface()->supports_rssi_with_inquiry_results()) {
+      btm_sort_inq_result();
+    }
+
+    btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE;
+    btm_cb.btm_inq_vars.p_inq_results_cb = nullptr;
+    btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+    btm_cb.btm_inq_vars.inq_counter++;
+
+    if (btm_cb.btm_inq_vars.p_inq_cmpl_cb != nullptr) {
+      LOG_DEBUG("%s Sending cancel inquiry completion to upper layer",
+                __func__);
+      (btm_cb.btm_inq_vars.p_inq_cmpl_cb)(
+          (tBTM_INQUIRY_CMPL*)&btm_cb.btm_inq_vars.inq_cmpl_info);
+      btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+    }
+  }
+  if (btm_cb.btm_inq_vars.inqparms.mode == BTM_INQUIRY_NONE &&
+      btm_cb.btm_inq_vars.scan_type == INQ_GENERAL) {
+    btm_cb.btm_inq_vars.scan_type = INQ_NONE;
+  }
   return BTM_SUCCESS;
 }
 
@@ -330,40 +661,42 @@
 
   switch (transport) {
     case BT_TRANSPORT_LE:
-      status = shim_btm.ReadLeRemoteDeviceName(raw_address, callback);
+      status = Stack::GetInstance()->GetBtm()->ReadLeRemoteDeviceName(
+          raw_address, callback);
       break;
     case BT_TRANSPORT_BR_EDR:
-      status = shim_btm.ReadClassicRemoteDeviceName(raw_address, callback);
+      status = Stack::GetInstance()->GetBtm()->ReadClassicRemoteDeviceName(
+          raw_address, callback);
       break;
     default:
-      LOG_WARN(LOG_TAG, "%s Unspecified transport:%d", __func__, transport);
+      LOG_WARN("%s Unspecified transport:%d", __func__, transport);
       break;
   }
   return status;
 }
 
 tBTM_STATUS bluetooth::shim::BTM_CancelRemoteDeviceName(void) {
-  return shim_btm.CancelAllReadRemoteDeviceName();
+  return Stack::GetInstance()->GetBtm()->CancelAllReadRemoteDeviceName();
 }
 
 tBTM_INQ_INFO* bluetooth::shim::BTM_InqDbRead(const RawAddress& p_bda) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return nullptr;
 }
 
 tBTM_INQ_INFO* bluetooth::shim::BTM_InqDbFirst(void) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return nullptr;
 }
 
 tBTM_INQ_INFO* bluetooth::shim::BTM_InqDbNext(tBTM_INQ_INFO* p_cur) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_cur != nullptr);
   return nullptr;
 }
 
 tBTM_STATUS bluetooth::shim::BTM_ClearInqDb(const RawAddress* p_bda) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   if (p_bda == nullptr) {
     // clear all entries
   } else {
@@ -372,40 +705,34 @@
   return BTM_NO_RESOURCES;
 }
 
-tBTM_STATUS bluetooth::shim::BTM_ReadInquiryRspTxPower(tBTM_CMPL_CB* p_cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  CHECK(p_cb != nullptr);
-  return BTM_NO_RESOURCES;
-}
-
 tBTM_STATUS bluetooth::shim::BTM_WriteEIR(BT_HDR* p_buff) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_buff != nullptr);
   return BTM_NO_RESOURCES;
 }
 
 bool bluetooth::shim::BTM_HasEirService(const uint32_t* p_eir_uuid,
                                         uint16_t uuid16) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_eir_uuid != nullptr);
   return false;
 }
 
 tBTM_EIR_SEARCH_RESULT bluetooth::shim::BTM_HasInquiryEirService(
     tBTM_INQ_RESULTS* p_results, uint16_t uuid16) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_results != nullptr);
   return BTM_EIR_UNKNOWN;
 }
 
 void bluetooth::shim::BTM_AddEirService(uint32_t* p_eir_uuid, uint16_t uuid16) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_eir_uuid != nullptr);
 }
 
 void bluetooth::shim::BTM_RemoveEirService(uint32_t* p_eir_uuid,
                                            uint16_t uuid16) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_eir_uuid != nullptr);
 }
 
@@ -413,7 +740,7 @@
                                                      uint8_t** p,
                                                      uint8_t max_num_uuid16,
                                                      uint8_t* p_num_uuid16) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_eir_uuid != nullptr);
   CHECK(p != nullptr);
   CHECK(*p != nullptr);
@@ -426,7 +753,7 @@
                                             uint8_t* p_num_uuid,
                                             uint8_t* p_uuid_list,
                                             uint8_t max_num_uuid) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_eir != nullptr);
   CHECK(p_num_uuid != nullptr);
   CHECK(p_uuid_list != nullptr);
@@ -437,21 +764,21 @@
                                           BD_NAME bd_name,
                                           tBT_DEVICE_TYPE dev_type,
                                           tBLE_ADDR_TYPE addr_type) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 bool bluetooth::shim::BTM_SecAddBleKey(const RawAddress& bd_addr,
                                        tBTM_LE_KEY_VALUE* p_le_key,
                                        tBTM_LE_KEY_TYPE key_type) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_le_key != nullptr);
   return false;
 }
 
 void bluetooth::shim::BTM_BleLoadLocalKeys(uint8_t key_type,
                                            tBTM_BLE_LOCAL_KEYS* p_key) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_key != nullptr);
 }
 
@@ -459,74 +786,74 @@
 
 /** Returns local device encryption root (ER) */
 const Octet16& bluetooth::shim::BTM_GetDeviceEncRoot() {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return bogus_root;
 }
 
 /** Returns local device identity root (IR). */
 const Octet16& bluetooth::shim::BTM_GetDeviceIDRoot() {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return bogus_root;
 }
 
 /** Return local device DHK. */
 const Octet16& bluetooth::shim::BTM_GetDeviceDHK() {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return bogus_root;
 }
 
 void bluetooth::shim::BTM_ReadConnectionAddr(const RawAddress& remote_bda,
                                              RawAddress& local_conn_addr,
                                              tBLE_ADDR_TYPE* p_addr_type) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_addr_type != nullptr);
 }
 
 bool bluetooth::shim::BTM_IsBleConnection(uint16_t conn_handle) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 bool bluetooth::shim::BTM_ReadRemoteConnectionAddr(
     const RawAddress& pseudo_addr, RawAddress& conn_addr,
     tBLE_ADDR_TYPE* p_addr_type) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_addr_type != nullptr);
   return false;
 }
 
 void bluetooth::shim::BTM_SecurityGrant(const RawAddress& bd_addr,
                                         uint8_t res) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_BlePasskeyReply(const RawAddress& bd_addr,
                                           uint8_t res, uint32_t passkey) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_BleConfirmReply(const RawAddress& bd_addr,
                                           uint8_t res) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_BleOobDataReply(const RawAddress& bd_addr,
                                           uint8_t res, uint8_t len,
                                           uint8_t* p_data) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_data != nullptr);
 }
 
 void bluetooth::shim::BTM_BleSecureConnectionOobDataReply(
     const RawAddress& bd_addr, uint8_t* p_c, uint8_t* p_r) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_c != nullptr);
   CHECK(p_r != nullptr);
 }
 
 void bluetooth::shim::BTM_BleSetConnScanParams(uint32_t scan_interval,
                                                uint32_t scan_window) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_BleSetPrefConnParams(const RawAddress& bd_addr,
@@ -534,27 +861,27 @@
                                                uint16_t max_conn_int,
                                                uint16_t slave_latency,
                                                uint16_t supervision_tout) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_ReadDevInfo(const RawAddress& remote_bda,
                                       tBT_DEVICE_TYPE* p_dev_type,
                                       tBLE_ADDR_TYPE* p_addr_type) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_dev_type != nullptr);
   CHECK(p_addr_type != nullptr);
 }
 
 bool bluetooth::shim::BTM_ReadConnectedTransportAddress(
     RawAddress* remote_bda, tBT_TRANSPORT transport) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(remote_bda != nullptr);
   return false;
 }
 
 void bluetooth::shim::BTM_BleReceiverTest(uint8_t rx_freq,
                                           tBTM_CMPL_CB* p_cmd_cmpl_cback) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_cmd_cmpl_cback != nullptr);
 }
 
@@ -562,47 +889,47 @@
                                              uint8_t test_data_len,
                                              uint8_t packet_payload,
                                              tBTM_CMPL_CB* p_cmd_cmpl_cback) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_cmd_cmpl_cback != nullptr);
 }
 
 void bluetooth::shim::BTM_BleTestEnd(tBTM_CMPL_CB* p_cmd_cmpl_cback) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_cmd_cmpl_cback != nullptr);
 }
 
 bool bluetooth::shim::BTM_UseLeLink(const RawAddress& raw_address) {
-  return shim_btm.IsLeAclConnected(raw_address);
+  return Stack::GetInstance()->GetBtm()->IsLeAclConnected(raw_address);
 }
 
 tBTM_STATUS bluetooth::shim::BTM_SetBleDataLength(const RawAddress& bd_addr,
                                                   uint16_t tx_pdu_length) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return BTM_NO_RESOURCES;
 }
 
 void bluetooth::shim::BTM_BleReadPhy(
     const RawAddress& bd_addr,
     base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 tBTM_STATUS bluetooth::shim::BTM_BleSetDefaultPhy(uint8_t all_phys,
                                                   uint8_t tx_phys,
                                                   uint8_t rx_phys) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return BTM_NO_RESOURCES;
 }
 
 void bluetooth::shim::BTM_BleSetPhy(const RawAddress& bd_addr, uint8_t tx_phys,
                                     uint8_t rx_phys, uint16_t phy_options) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 bool bluetooth::shim::BTM_BleDataSignature(const RawAddress& bd_addr,
                                            uint8_t* p_text, uint16_t len,
                                            BLE_SIGNATURE signature) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_text != nullptr);
   return false;
 }
@@ -611,7 +938,7 @@
                                              uint8_t* p_orig, uint16_t len,
                                              uint32_t counter,
                                              uint8_t* p_comp) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_orig != nullptr);
   CHECK(p_comp != nullptr);
   return false;
@@ -620,7 +947,7 @@
 bool bluetooth::shim::BTM_GetLeSecurityState(const RawAddress& bd_addr,
                                              uint8_t* p_le_dev_sec_flags,
                                              uint8_t* p_le_key_size) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   CHECK(p_le_dev_sec_flags != nullptr);
   CHECK(p_le_key_size != nullptr);
   return false;
@@ -628,12 +955,12 @@
 
 bool bluetooth::shim::BTM_BleSecurityProcedureIsRunning(
     const RawAddress& bd_addr) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 uint8_t bluetooth::shim::BTM_BleGetSupportedKeySize(const RawAddress& bd_addr) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return 0;
 }
 
@@ -645,33 +972,33 @@
                                            tBTM_BLE_PF_FILT_INDEX filt_index,
                                            std::vector<uint8_t> name,
                                            tBTM_BLE_PF_CFG_CBACK cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_LE_PF_srvc_data(tBTM_BLE_SCAN_COND_OP action,
                                           tBTM_BLE_PF_FILT_INDEX filt_index) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_LE_PF_manu_data(
     tBTM_BLE_SCAN_COND_OP action, tBTM_BLE_PF_FILT_INDEX filt_index,
     uint16_t company_id, uint16_t company_id_mask, std::vector<uint8_t> data,
     std::vector<uint8_t> data_mask, tBTM_BLE_PF_CFG_CBACK cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_LE_PF_srvc_data_pattern(
     tBTM_BLE_SCAN_COND_OP action, tBTM_BLE_PF_FILT_INDEX filt_index,
     std::vector<uint8_t> data, std::vector<uint8_t> data_mask,
     tBTM_BLE_PF_CFG_CBACK cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_LE_PF_addr_filter(tBTM_BLE_SCAN_COND_OP action,
                                             tBTM_BLE_PF_FILT_INDEX filt_index,
                                             tBLE_BD_ADDR addr,
                                             tBTM_BLE_PF_CFG_CBACK cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_LE_PF_uuid_filter(tBTM_BLE_SCAN_COND_OP action,
@@ -681,36 +1008,100 @@
                                             tBTM_BLE_PF_LOGIC_TYPE cond_logic,
                                             const bluetooth::Uuid& uuid_mask,
                                             tBTM_BLE_PF_CFG_CBACK cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_LE_PF_set(tBTM_BLE_PF_FILT_INDEX filt_index,
                                     std::vector<ApcfCommand> commands,
                                     tBTM_BLE_PF_CFG_CBACK cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_LE_PF_clear(tBTM_BLE_PF_FILT_INDEX filt_index,
                                       tBTM_BLE_PF_CFG_CBACK cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_BleAdvFilterParamSetup(
     int action, tBTM_BLE_PF_FILT_INDEX filt_index,
     std::unique_ptr<btgatt_filt_param_setup_t> p_filt_params,
     tBTM_BLE_PF_PARAM_CB cb) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_BleUpdateAdvFilterPolicy(tBTM_BLE_AFP adv_policy) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 void bluetooth::shim::BTM_BleEnableDisableFilterFeature(
     uint8_t enable, tBTM_BLE_PF_STATUS_CBACK p_stat_cback) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
 }
 
 uint8_t bluetooth::shim::BTM_BleMaxMultiAdvInstanceCount() {
-  return shim_btm.GetNumberOfAdvertisingInstances();
+  return Stack::GetInstance()->GetBtm()->GetNumberOfAdvertisingInstances();
+}
+
+bool bluetooth::shim::BTM_BleLocalPrivacyEnabled(void) {
+  return controller_get_interface()->supports_ble_privacy();
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SecBond(const RawAddress& bd_addr,
+                                         tBLE_ADDR_TYPE addr_type,
+                                         tBT_TRANSPORT transport,
+                                         int device_type) {
+  return Stack::GetInstance()->GetBtm()->CreateBond(bd_addr, addr_type,
+                                                    transport, device_type);
+}
+
+bool bluetooth::shim::BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info) {
+  CHECK(p_cb_info != nullptr);
+  LOG_DEBUG("%s Registering security application", __func__);
+
+  if (p_cb_info->p_authorize_callback == nullptr) {
+    LOG_INFO("UNIMPLEMENTED %s authorize_callback", __func__);
+  }
+
+  if (p_cb_info->p_pin_callback == nullptr) {
+    LOG_INFO("UNIMPLEMENTED %s pin_callback", __func__);
+  }
+
+  if (p_cb_info->p_link_key_callback == nullptr) {
+    LOG_INFO("UNIMPLEMENTED %s link_key_callback", __func__);
+  }
+
+  if (p_cb_info->p_auth_complete_callback == nullptr) {
+    LOG_INFO("UNIMPLEMENTED %s auth_complete_callback", __func__);
+  }
+
+  if (p_cb_info->p_bond_cancel_cmpl_callback == nullptr) {
+    LOG_INFO("UNIMPLEMENTED %s bond_cancel_complete_callback", __func__);
+  }
+
+  if (p_cb_info->p_le_callback == nullptr) {
+    LOG_INFO("UNIMPLEMENTED %s le_callback", __func__);
+  }
+
+  if (p_cb_info->p_le_key_callback == nullptr) {
+    LOG_INFO("UNIMPLEMENTED %s le_key_callback", __func__);
+  }
+
+  return true;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SecBondCancel(const RawAddress& bd_addr) {
+  if (Stack::GetInstance()->GetBtm()->CancelBond(bd_addr)) {
+    return BTM_SUCCESS;
+  } else {
+    return BTM_UNKNOWN_ADDR;
+  }
+}
+
+bool bluetooth::shim::BTM_SecDeleteDevice(const RawAddress& bd_addr) {
+  return Stack::GetInstance()->GetBtm()->RemoveBond(bd_addr);
+}
+
+uint16_t bluetooth::shim::BTM_GetHCIConnHandle(const RawAddress& remote_bda,
+                                               tBT_TRANSPORT transport) {
+  return Stack::GetInstance()->GetBtm()->GetAclHandle(remote_bda, transport);
 }
diff --git a/main/shim/btm_api.h b/main/shim/btm_api.h
index 905fcfa..d4af687 100644
--- a/main/shim/btm_api.h
+++ b/main/shim/btm_api.h
@@ -64,45 +64,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetPeriodicInquiryMode
- *
- * Description      This function is called to set the device periodic inquiry
- *                  mode. If the duration is zero, the periodic inquiry mode is
- *                  cancelled.
- *
- * Parameters:      p_inqparms - pointer to the inquiry information
- *                      mode - GENERAL or LIMITED inquiry
- *                      duration - length in 1.28 sec intervals (If '0', the
- *                                 inquiry is CANCELLED)
- *                      max_resps - maximum amount of devices to search for
- *                                  before ending the inquiry
- *                      filter_cond_type - BTM_CLR_INQUIRY_FILTER,
- *                                         BTM_FILTER_COND_DEVICE_CLASS, or
- *                                         BTM_FILTER_COND_BD_ADDR
- *                      filter_cond - value for the filter (based on
- *                                                          filter_cond_type)
- *
- *                  max_delay - maximum amount of time between successive
- *                              inquiries
- *                  min_delay - minimum amount of time between successive
- *                              inquiries
- *                  p_results_cb - callback returning pointer to results
- *                              (tBTM_INQ_RESULTS)
- *
- * Returns          BTM_CMD_STARTED if successfully started
- *                  BTM_ILLEGAL_VALUE if a bad parameter is detected
- *                  BTM_NO_RESOURCES if could not allocate a message buffer
- *                  BTM_SUCCESS - if cancelling the periodic inquiry
- *                  BTM_BUSY - if an inquiry is already active
- *                  BTM_WRONG_MODE if the device is not up.
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetPeriodicInquiryMode(tBTM_INQ_PARMS* p_inqparms,
-                                       uint16_t max_delay, uint16_t min_delay,
-                                       tBTM_INQ_RESULTS_CB* p_results_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_SetDiscoverability
  *
  * Description      This function is called to set the device into or out of
@@ -380,20 +341,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadInquiryRspTxPower
- *
- * Description      This command will read the inquiry Transmit Power level used
- *                  to transmit the FHS and EIR data packets.
- *                  This can be used directly in the Tx Power Level EIR data
- *                  type.
- *
- * Returns          BTM_SUCCESS if successful
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ReadInquiryRspTxPower(tBTM_CMPL_CB* p_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_WriteEIR
  *
  * Description      This function is called to write EIR data to controller.
@@ -1278,53 +1225,6 @@
  ******************************************************************************/
 uint8_t* BTM_ReadRemoteFeatures(const RawAddress& addr);
 
-/*******************************************************************************
- *
- * Function         BTM_ReadRemoteExtendedFeatures
- *
- * Description      This function is called to read a specific extended features
- *                  page of the remote device
- *
- *                  Note1: The size of device features mask page is
- *                  BTM_FEATURE_BYTES_PER_PAGE bytes.
- *                  Note2: The valid device features mask page number depends on
- *                  the remote device capabilities. It is expected to be in the
- *                  range [0 - BTM_EXT_FEATURES_PAGE_MAX].
-
- * Returns          pointer to the remote extended features mask
- *                  or NULL if page_number is not valid
- *
- ******************************************************************************/
-uint8_t* BTM_ReadRemoteExtendedFeatures(const RawAddress& addr,
-                                        uint8_t page_number);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadNumberRemoteFeaturesPages
- *
- * Description      This function is called to retrieve the number of feature
- *                  pages read from the remote device
- *
- * Returns          number of features pages read from the remote device
- *
- ******************************************************************************/
-uint8_t BTM_ReadNumberRemoteFeaturesPages(const RawAddress& addr);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadAllRemoteFeatures
- *
- * Description      Read all the features of the remote device
- *
- * Returns          pointer to the byte[0] of the page[0] of the remote device
- *                  feature mask.
- *
- * Note:            the function returns the pointer to the array of the size
- *                  BTM_FEATURE_BYTES_PER_PAGE * (BTM_EXT_FEATURES_PAGE_MAX + 1)
- *
- ******************************************************************************/
-uint8_t* BTM_ReadAllRemoteFeatures(const RawAddress& addr);
-
 /*****************************************************************************
  *  ACL CHANNEL MANAGEMENT FUNCTIONS
  ****************************************************************************/
@@ -1507,24 +1407,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadLinkQuality
- *
- * Description      This function is called to read the link quality.
- *                  The value of the link quality is returned in the callback.
- *                  (tBTM_LINK_QUALITY_RESULT)
- *
- * Returns          BTM_CMD_STARTED if command issued to controller.
- *                  BTM_NO_RESOURCES if memory couldn't be allocated to issue
- *                                   the command
- *                  BTM_UNKNOWN_ADDR if no active link with bd addr specified
- *                  BTM_BUSY if command is already in progress
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ReadLinkQuality(const RawAddress& remote_bda,
-                                tBTM_CMPL_CB* p_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_RegBusyLevelNotif
  *
  * Description      This function is called to register a callback to receive
@@ -1538,18 +1420,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_AclRegisterForChanges
- *
- * Description      This function is called to register a callback to receive
- *                  ACL database change events, i.e. new connection or removed.
- *
- * Returns          BTM_SUCCESS if successfully initiated, otherwise error
- *
- ******************************************************************************/
-tBTM_STATUS BTM_AclRegisterForChanges(tBTM_ACL_DB_CHANGE_CB* p_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_GetNumAclLinks
  *
  * Description      This function is called to count the number of
@@ -1560,18 +1430,6 @@
  ******************************************************************************/
 uint16_t BTM_GetNumAclLinks(void);
 
-/*******************************************************************************
- *
- * Function         BTM_SetQoS
- *
- * Description      This function is called to setup QoS
- *
- * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetQoS(const RawAddress& bd, FLOW_SPEC* p_flow,
-                       tBTM_CMPL_CB* p_cb);
-
 /*****************************************************************************
  *  (e)SCO CHANNEL MANAGEMENT FUNCTIONS
  ****************************************************************************/
@@ -1609,71 +1467,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetScoPacketTypes
- *
- * Description      This function is called to set the packet types used for
- *                  a specific SCO connection,
- *
- * Parameters       pkt_types - One or more of the following
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *
- *                  BTM_SCO_LINK_ALL_MASK   - enables all supported types
- *
- * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetScoPacketTypes(uint16_t sco_inx, uint16_t pkt_types);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoPacketTypes
- *
- * Description      This function is read the packet types used for a specific
- *                  SCO connection.
- *
- * Returns       One or more of the following (bitmask)
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *
- * Returns          packet types supported for the connection
- *
- ******************************************************************************/
-uint16_t BTM_ReadScoPacketTypes(uint16_t sco_inx);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadDeviceScoPacketTypes
- *
- * Description      This function is read the SCO packet types that
- *                  the device supports.
- *
- * Returns          packet types supported by the device.
- *
- ******************************************************************************/
-uint16_t BTM_ReadDeviceScoPacketTypes(void);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoHandle
- *
- * Description      Reead the HCI handle used for a specific SCO connection,
- *
- * Returns          handle for the connection, or 0xFFFF if invalid SCO index.
- *
- ******************************************************************************/
-uint16_t BTM_ReadScoHandle(uint16_t sco_inx);
-
-/*******************************************************************************
- *
  * Function         BTM_ReadScoBdAddr
  *
  * Description      This function is read the remote BD Address for a specific
@@ -1686,19 +1479,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadScoDiscReason
- *
- * Description      This function is returns the reason why an (e)SCO connection
- *                  has been removed. It contains the value until read, or until
- *                  another (e)SCO connection has disconnected.
- *
- * Returns          HCI reason or BTM_INVALID_SCO_DISC_REASON if not set.
- *
- ******************************************************************************/
-uint16_t BTM_ReadScoDiscReason(void);
-
-/*******************************************************************************
- *
  * Function         BTM_SetEScoMode
  *
  * Description      This function sets up the negotiated parameters for SCO or
@@ -1714,19 +1494,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetWBSCodec
- *
- * Description      This function sends command to the controller to setup
- *                  WBS codec for the upcoming eSCO connection.
- *
- * Returns          BTM_SUCCESS.
- *
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetWBSCodec(tBTM_SCO_CODEC_TYPE codec_type);
-
-/*******************************************************************************
- *
  * Function         BTM_RegForEScoEvts
  *
  * Description      This function registers a SCO event callback with the
@@ -1742,29 +1509,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadEScoLinkParms
- *
- * Description      This function returns the current eSCO link parameters for
- *                  the specified handle.  This can be called anytime a
- *                  connection is active, but is typically called after
- *                  receiving the SCO opened callback.
- *
- *                  Note: If called over a 1.1 controller, only the packet types
- *                        field has meaning.
- *                  Note: If the upper layer doesn't know the current sco index,
- *                  BTM_FIRST_ACTIVE_SCO_INDEX can be used as the first
- *                  parameter to find the first active SCO index
- *
- * Returns          BTM_SUCCESS if returned data is valid connection.
- *                  BTM_ILLEGAL_VALUE if no connection for specified sco_inx.
- *                  BTM_MODE_UNSUPPORTED if local controller does not support
- *                      1.2 specification.
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ReadEScoLinkParms(uint16_t sco_inx, tBTM_ESCO_DATA* p_parms);
-
-/*******************************************************************************
- *
  * Function         BTM_ChangeEScoLinkParms
  *
  * Description      This function requests renegotiation of the parameters on
@@ -1836,19 +1580,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecRegisterLinkKeyNotificationCallback
- *
- * Description      Profiles can register to be notified when a new Link Key
- *                  is generated per connection.
- *
- * Returns          true if registered OK, else false
- *
- ******************************************************************************/
-bool BTM_SecRegisterLinkKeyNotificationCallback(
-    tBTM_LINK_KEY_CALLBACK* p_callback);
-
-/*******************************************************************************
- *
  * Function         BTM_SecAddRmtNameNotifyCallback
  *
  * Description      Profiles can register to be notified when name of the
@@ -1943,24 +1674,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetSecureConnectionsOnly
- *
- * Description      Enable or disable default treatment for Mode 4 Level 0
- *                  services
- *
- * Parameter        secure_connections_only_mode - (true or false)
- *                  true means that the device should treat Mode 4 Level 0
- *                  services as services of other levels.
- *                  false means that the device should provide default
- *                  treatment for Mode 4 Level 0 services.
- *
- * Returns          void
- *
- ******************************************************************************/
-void BTM_SetSecureConnectionsOnly(bool secure_connections_only_mode);
-
-/*******************************************************************************
- *
  * Function         BTM_SetSecurityLevel
  *
  * Description      Register service security level with Security Manager.  Each
@@ -2044,21 +1757,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecGetDeviceLinkKey
- *
- * Description      This function is called to obtain link key for the device
- *                  it returns BTM_SUCCESS if link key is available, or
- *                  BTM_UNKNOWN_ADDR if Security Manager does not know about
- *                  the device or device record does not contain link key info
- *
- * Returns          BTM_SUCCESS if successful, otherwise error code
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SecGetDeviceLinkKey(const RawAddress& bd_addr,
-                                    LinkKey* link_key);
-
-/*******************************************************************************
- *
  * Function         BTM_SecGetDeviceLinkKeyType
  *
  * Description      This function is called to obtain link key type for the
@@ -2099,26 +1797,10 @@
  *
  * Function         BTM_SecBond
  *
- * Description      This function is called to perform bonding with peer device.
- *
- * Parameters:      bd_addr      - Address of the device to bond
- *                  pin_len      - length in bytes of the PIN Code
- *                  p_pin        - pointer to array with the PIN Code
- *                  trusted_mask - bitwise OR of trusted services
- *                                 (array of uint32_t)
- * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, uint8_t pin_len,
-                        uint8_t* p_pin, uint32_t trusted_mask[]);
-
-/*******************************************************************************
- *
- * Function         BTM_SecBondByTransport
- *
  * Description      Perform bonding by designated transport
  *
  * Parameters:      bd_addr      - Address of the device to bond
+ *                  addr_type    - address type for LE transport
  *                  pin_len      - length in bytes of the PIN Code
  *                  p_pin        - pointer to array with the PIN Code
  *                  trusted_mask - bitwise OR of trusted services
@@ -2129,9 +1811,8 @@
  * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
  *
  ******************************************************************************/
-tBTM_STATUS BTM_SecBondByTransport(const RawAddress& bd_addr,
-                                   tBT_TRANSPORT transport, uint8_t pin_len,
-                                   uint8_t* p_pin, uint32_t trusted_mask[]);
+tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                        tBT_TRANSPORT transport, int device_type);
 
 /*******************************************************************************
  *
@@ -2270,27 +1951,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_BuildOobData
- *
- * Description      This function is called to build the OOB data payload to
- *                  be sent over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  max_len - p_data size.
- *                  c       - simple pairing Hash C.
- *                  r       - simple pairing Randomizer  C.
- *                  name_len- 0, local device name would not be included.
- *                            otherwise, the local device name is included for
- *                            up to this specified length
- *
- * Returns          Number of bytes in p_data.
- *
- ******************************************************************************/
-uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len, const Octet16& c,
-                          const Octet16& r, uint8_t name_len);
-
-/*******************************************************************************
- *
  * Function         BTM_BothEndsSupportSecureConnections
  *
  * Description      This function is called to check if both the local device
@@ -2323,23 +1983,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadOobData
- *
- * Description      This function is called to parse the OOB data payload
- *                  received over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  eir_tag - The associated EIR tag to read the data.
- *                  *p_len(output) - the length of the data with the given tag.
- *
- * Returns          the beginning of the data with the given tag.
- *                  NULL, if the tag is not found.
- *
- ******************************************************************************/
-uint8_t* BTM_ReadOobData(uint8_t* p_data, uint8_t eir_tag, uint8_t* p_len);
-
-/*******************************************************************************
- *
  * Function         BTM_SecReadDevName
  *
  * Description      Looks for the device name in the security database for the
@@ -2454,95 +2097,6 @@
 tBTM_STATUS BTM_DeleteStoredLinkKey(const RawAddress* bd_addr,
                                     tBTM_CMPL_CB* p_cb);
 
-/*****************************************************************************
- *  SCO OVER HCI
- ****************************************************************************/
-/*******************************************************************************
- *
- * Function         BTM_ConfigScoPath
- *
- * Description      This function enable/disable SCO over HCI and registers SCO
- *                  data callback if SCO over HCI is enabled.
- *
- * Parameter        path: SCO or HCI
- *                  p_sco_data_cb: callback function or SCO data if path is set
- *                                 to transport.
- *                  p_pcm_param: pointer to the PCM interface parameter. If a
- *                               NULL pointer is used, the PCM parameter
- *                               maintained in the control block will be used;
- *                               otherwise update the control block value.
- *                  err_data_rpt: Lisbon feature to enable the erronous data
- *                                report or not.
- *
- * Returns          BTM_SUCCESS if the successful.
- *                  BTM_NO_RESOURCES: no rsource to start the command.
- *                  BTM_ILLEGAL_VALUE: invalid callback function pointer.
- *                  BTM_CMD_STARTED : Command sent. Waiting for command
- *                                    complete event.
- *
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ConfigScoPath(esco_data_path_t path,
-                              tBTM_SCO_DATA_CB* p_sco_data_cb,
-                              tBTM_SCO_PCM_PARAM* p_pcm_param,
-                              bool err_data_rpt);
-
-/*******************************************************************************
- *
- * Function         BTM_WriteScoData
- *
- * Description      This function write SCO data to a specified instance. The
- *                  data to be written p_buf needs to carry an offset of
- *                  HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not
- *                  exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is
- *                  set to 60 and is configurable. Data longer than the maximum
- *                  bytes will be truncated.
- *
- * Returns          BTM_SUCCESS: data write is successful
- *                  BTM_ILLEGAL_VALUE: SCO data contains illegal offset value.
- *                  BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO
- *                                      packet size.
- *                  BTM_NO_RESOURCES: no resources.
- *                  BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is
- *                                    not routed via HCI.
- *
- *
- ******************************************************************************/
-tBTM_STATUS BTM_WriteScoData(uint16_t sco_inx, BT_HDR* p_buf);
-
-/*******************************************************************************
- *
- * Function         BTM_SetARCMode
- *
- * Description      Send Audio Routing Control command.
- *
- * Returns          void
- *
- ******************************************************************************/
-void BTM_SetARCMode(uint8_t iface, uint8_t arc_mode,
-                    tBTM_VSC_CMPL_CB* p_arc_cb);
-
-/*******************************************************************************
- *
- * Function         BTM_PCM2Setup_Write
- *
- * Description      Send PCM2_Setup write command.
- *
- * Returns          void
- *
- ******************************************************************************/
-void BTM_PCM2Setup_Write(bool clk_master, tBTM_VSC_CMPL_CB* p_arc_cb);
-
-/*******************************************************************************
- *
- * Function         BTM_PM_ReadControllerState
- *
- * Description      This function is called to obtain the controller state
- *
- * Returns          Controller state (BTM_CONTRL_ACTIVE, BTM_CONTRL_SCAN, and
- *                                    BTM_CONTRL_IDLE)
- *
- ******************************************************************************/
 tBTM_CONTRL_STATE BTM_PM_ReadControllerState(void);
 
 /**
diff --git a/main/shim/config.cc b/main/shim/config.cc
new file mode 100644
index 0000000..ceb03cd
--- /dev/null
+++ b/main/shim/config.cc
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_shim_storage"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+
+#include "gd/os/log.h"
+#include "gd/storage/config_cache_helper.h"
+#include "gd/storage/storage_module.h"
+#include "main/shim/config.h"
+#include "main/shim/entry.h"
+
+using ::bluetooth::shim::GetStorage;
+using ::bluetooth::storage::ConfigCacheHelper;
+
+namespace bluetooth {
+namespace shim {
+
+bool BtifConfigInterface::HasSection(const std::string& section) {
+  return GetStorage()->GetConfigCache()->HasSection(section);
+}
+
+bool BtifConfigInterface::HasProperty(const std::string& section,
+                                      const std::string& property) {
+  return GetStorage()->GetConfigCache()->HasProperty(section, property);
+}
+
+bool BtifConfigInterface::GetInt(const std::string& section,
+                                 const std::string& property, int* value) {
+  ASSERT(value != nullptr);
+  auto ret = ConfigCacheHelper::FromConfigCache(*GetStorage()->GetConfigCache())
+                 .GetInt(section, property);
+  if (ret) {
+    *value = *ret;
+  }
+  return ret.has_value();
+}
+
+bool BtifConfigInterface::SetInt(const std::string& section,
+                                 const std::string& property, int value) {
+  ConfigCacheHelper::FromConfigCache(*GetStorage()->GetConfigCache())
+      .SetInt(section, property, value);
+  return true;
+}
+
+bool BtifConfigInterface::GetUint64(const std::string& section,
+                                    const std::string& property,
+                                    uint64_t* value) {
+  ASSERT(value != nullptr);
+  auto ret = ConfigCacheHelper::FromConfigCache(*GetStorage()->GetConfigCache())
+                 .GetUint64(section, property);
+  if (ret) {
+    *value = *ret;
+  }
+  return ret.has_value();
+}
+
+bool BtifConfigInterface::SetUint64(const std::string& section,
+                                    const std::string& property,
+                                    uint64_t value) {
+  ConfigCacheHelper::FromConfigCache(*GetStorage()->GetConfigCache())
+      .SetUint64(section, property, value);
+  return true;
+}
+
+bool BtifConfigInterface::GetStr(const std::string& section,
+                                 const std::string& property, char* value,
+                                 int* size_bytes) {
+  ASSERT(value != nullptr);
+  ASSERT(size_bytes != nullptr);
+  auto str = GetStorage()->GetConfigCache()->GetProperty(section, property);
+  if (!str) {
+    return false;
+  }
+  if (*size_bytes == 0) {
+    return true;
+  }
+  // std::string::copy does not null-terminate resultant string by default
+  // avoided using strlcpy to prevent extra dependency
+  *size_bytes = str->copy(value, (*size_bytes - 1));
+  value[*size_bytes] = '\0';
+  *size_bytes += 1;
+  return true;
+}
+
+std::optional<std::string> BtifConfigInterface::GetStr(
+    const std::string& section, const std::string& property) {
+  return GetStorage()->GetConfigCache()->GetProperty(section, property);
+}
+
+bool BtifConfigInterface::SetStr(const std::string& section,
+                                 const std::string& property,
+                                 const std::string& value) {
+  GetStorage()->GetConfigCache()->SetProperty(section, property, value);
+  return true;
+}
+
+// TODO: implement encrypted read
+bool BtifConfigInterface::GetBin(const std::string& section,
+                                 const std::string& property, uint8_t* value,
+                                 size_t* length) {
+  ASSERT(value != nullptr);
+  ASSERT(length != nullptr);
+  auto value_vec =
+      ConfigCacheHelper::FromConfigCache(*GetStorage()->GetConfigCache())
+          .GetBin(section, property);
+  if (!value_vec) {
+    return false;
+  }
+  *length = std::min(value_vec->size(), *length);
+  std::memcpy(value, value_vec->data(), *length);
+  return true;
+}
+size_t BtifConfigInterface::GetBinLength(const std::string& section,
+                                         const std::string& property) {
+  auto value_vec =
+      ConfigCacheHelper::FromConfigCache(*GetStorage()->GetConfigCache())
+          .GetBin(section, property);
+  if (!value_vec) {
+    return 0;
+  }
+  return value_vec->size();
+}
+bool BtifConfigInterface::SetBin(const std::string& section,
+                                 const std::string& property,
+                                 const uint8_t* value, size_t length) {
+  ASSERT(value != nullptr);
+  std::vector<uint8_t> value_vec(value, value + length);
+  ConfigCacheHelper::FromConfigCache(*GetStorage()->GetConfigCache())
+      .SetBin(section, property, value_vec);
+  return true;
+}
+bool BtifConfigInterface::RemoveProperty(const std::string& section,
+                                         const std::string& property) {
+  return GetStorage()->GetConfigCache()->RemoveProperty(section, property);
+}
+
+std::vector<std::string> BtifConfigInterface::GetPersistentDevices() {
+  return GetStorage()->GetConfigCache()->GetPersistentDevices();
+}
+
+void BtifConfigInterface::Save() { GetStorage()->SaveDelayed(); }
+
+void BtifConfigInterface::Flush() { GetStorage()->SaveImmediately(); }
+
+void BtifConfigInterface::Clear() { GetStorage()->GetConfigCache()->Clear(); }
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/config.h b/main/shim/config.h
new file mode 100644
index 0000000..6402bbf
--- /dev/null
+++ b/main/shim/config.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace bluetooth {
+namespace shim {
+
+class BtifConfigInterface {
+ public:
+  ~BtifConfigInterface() = default;
+  static bool HasSection(const std::string& section);
+  static bool HasProperty(const std::string& section,
+                          const std::string& property);
+  static bool GetInt(const std::string& section, const std::string& key,
+                     int* value);
+  static bool SetInt(const std::string& section, const std::string& key,
+                     int value);
+  static bool GetUint64(const std::string& section, const std::string& key,
+                        uint64_t* value);
+  static bool SetUint64(const std::string& section, const std::string& key,
+                        uint64_t value);
+  static bool GetStr(const std::string& section, const std::string& key,
+                     char* value, int* size_bytes);
+  static std::optional<std::string> GetStr(const std::string& section,
+                                           const std::string& key);
+  static bool SetStr(const std::string& section, const std::string& key,
+                     const std::string& value);
+  static bool GetBin(const std::string& section, const std::string& key,
+                     uint8_t* value, size_t* length);
+  static size_t GetBinLength(const std::string& section,
+                             const std::string& key);
+  static bool SetBin(const std::string& section, const std::string& key,
+                     const uint8_t* value, size_t length);
+  static bool RemoveProperty(const std::string& section,
+                             const std::string& key);
+  static std::vector<std::string> GetPersistentDevices();
+  static void Save();
+  static void Flush();
+  static void Clear();
+};
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/controller.cc b/main/shim/controller.cc
index d8b00d6..49d3d2e 100644
--- a/main/shim/controller.cc
+++ b/main/shim/controller.cc
@@ -23,6 +23,8 @@
 #include "osi/include/future.h"
 #include "osi/include/log.h"
 
+#include "hci/controller.h"
+
 using ::bluetooth::shim::GetController;
 
 constexpr uint8_t kPageZero = 0;
@@ -69,16 +71,17 @@
 } data_;
 
 static future_t* start_up(void) {
-  LOG_INFO(LOG_TAG, "%s Starting up", __func__);
+  LOG_INFO("%s Starting up", __func__);
   data_.ready = true;
 
-  std::string string_address = GetController()->GetControllerMacAddress();
+  std::string string_address =
+      GetController()->GetControllerMacAddress().ToString();
   RawAddress::FromString(string_address, data_.raw_address);
 
   data_.le_supported_states =
       bluetooth::shim::GetController()->GetControllerLeSupportedStates();
 
-  LOG_INFO(LOG_TAG, "Mac address:%s", string_address.c_str());
+  LOG_INFO("Mac address:%s", string_address.c_str());
 
   data_.phy = kPhyLe1M;
 
@@ -146,7 +149,8 @@
 }
 
 static bool supports_reading_remote_extended_features(void) {
-  return GetController()->IsCommandSupported(kReadRemoteExtendedFeatures);
+  return GetController()->IsSupported(
+      (bluetooth::hci::OpCode)kReadRemoteExtendedFeatures);
 }
 
 static bool supports_interlaced_inquiry_scan(void) {
@@ -170,13 +174,13 @@
 }
 
 static bool supports_enhanced_setup_synchronous_connection(void) {
-  return GetController()->IsCommandSupported(
-      kEnhancedSetupSynchronousConnection);
+  return GetController()->IsSupported(
+      (bluetooth::hci::OpCode)kEnhancedSetupSynchronousConnection);
 }
 
 static bool supports_enhanced_accept_synchronous_connection(void) {
-  return GetController()->IsCommandSupported(
-      kEnhancedAcceptSynchronousConnection);
+  return GetController()->IsSupported(
+      (bluetooth::hci::OpCode)kEnhancedAcceptSynchronousConnection);
 }
 
 static bool supports_ble(void) {
@@ -188,7 +192,8 @@
 }
 
 static bool supports_ble_set_privacy_mode() {
-  return GetController()->IsCommandSupported(kLeSetPrivacyMode);
+  return GetController()->IsSupported(
+      (bluetooth::hci::OpCode)kLeSetPrivacyMode);
 }
 
 static bool supports_ble_packet_extension(void) {
@@ -220,9 +225,9 @@
 }
 
 static uint16_t get_acl_data_size_ble(void) {
-  ::bluetooth::shim::LeBufferSize le_buffer_size =
+  ::bluetooth::hci::LeBufferSize le_buffer_size =
       GetController()->GetControllerLeBufferSize();
-  return le_buffer_size.le_data_packet_length;
+  return le_buffer_size.le_data_packet_length_;
 }
 
 static uint16_t get_acl_packet_size_classic(void) {
@@ -234,19 +239,18 @@
 }
 
 static uint16_t get_ble_suggested_default_data_length(void) {
-  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
+  LOG_WARN("%s TODO Unimplemented", __func__);
   return 0;
 }
 
 static uint16_t get_ble_maximum_tx_data_length(void) {
-  ::bluetooth::shim::LeMaximumDataLength le_maximum_data_length =
+  ::bluetooth::hci::LeMaximumDataLength le_maximum_data_length =
       GetController()->GetControllerLeMaximumDataLength();
-  return le_maximum_data_length.supported_max_tx_octets;
+  return le_maximum_data_length.supported_max_tx_octets_;
 }
 
 static uint16_t get_ble_maxium_advertising_data_length(void) {
-  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
-  return 0;
+  return GetController()->GetControllerLeMaximumAdvertisingDataLength();
 }
 
 static uint8_t get_ble_number_of_supported_advertising_sets(void) {
@@ -258,22 +262,20 @@
 }
 
 static uint8_t get_acl_buffer_count_ble(void) {
-  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
+  LOG_WARN("%s TODO Unimplemented", __func__);
   return 0;
 }
 
-static uint8_t get_ble_white_list_size(void) {
-  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
-  return 0;
+static uint8_t get_ble_connect_list_size(void) {
+  return GetController()->GetControllerLeConnectListSize();
 }
 
 static uint8_t get_ble_resolving_list_max_size(void) {
-  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
-  return 0;
+  return GetController()->GetControllerLeResolvingListSize();
 }
 
 static void set_ble_resolving_list_max_size(int resolving_list_max_size) {
-  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
+  LOG_WARN("%s TODO Unimplemented", __func__);
 }
 
 static uint8_t get_le_all_initiating_phys() { return data_.phy; }
@@ -324,7 +326,7 @@
     get_acl_buffer_count_classic,
     get_acl_buffer_count_ble,
 
-    get_ble_white_list_size,
+    get_ble_connect_list_size,
 
     get_ble_resolving_list_max_size,
     set_ble_resolving_list_max_size,
diff --git a/main/shim/dumpsys.cc b/main/shim/dumpsys.cc
new file mode 100644
index 0000000..2cd8597
--- /dev/null
+++ b/main/shim/dumpsys.cc
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_shim_storage"
+
+#include "main/shim/dumpsys.h"
+#include "main/shim/entry.h"
+#include "main/shim/shim.h"
+
+#include "shim/dumpsys.h"
+
+namespace {
+constexpr char kModuleName[] = "shim::legacy::dumpsys";
+static std::unordered_map<const void*, bluetooth::shim::DumpsysFunction>*
+    dumpsys_functions_;
+}  // namespace
+
+void bluetooth::shim::RegisterDumpsysFunction(const void* token,
+                                              DumpsysFunction func) {
+  dumpsys_functions_ =
+      new std::unordered_map<const void*, bluetooth::shim::DumpsysFunction>();
+  CHECK(dumpsys_functions_->find(token) == dumpsys_functions_->end());
+  dumpsys_functions_->insert({token, func});
+}
+
+void bluetooth::shim::UnregisterDumpsysFunction(const void* token) {
+  CHECK(dumpsys_functions_->find(token) != dumpsys_functions_->end());
+  dumpsys_functions_->erase(token);
+}
+
+void bluetooth::shim::Dump(int fd, const char** args) {
+  dprintf(fd, "%s Dumping shim legacy targets:%zd\n", kModuleName,
+          dumpsys_functions_->size());
+  for (auto& dumpsys : *dumpsys_functions_) {
+    dumpsys.second(fd);
+  }
+  if (bluetooth::shim::is_gd_stack_started_up()) {
+    bluetooth::shim::GetDumpsys()->Dump(fd, args);
+  } else {
+    dprintf(fd, "%s gd stack is enabled but not started\n", kModuleName);
+  }
+}
diff --git a/gd/shim/idiscoverability.h b/main/shim/dumpsys.h
similarity index 62%
rename from gd/shim/idiscoverability.h
rename to main/shim/dumpsys.h
index 2a2e4a8..33702ad 100644
--- a/gd/shim/idiscoverability.h
+++ b/main/shim/dumpsys.h
@@ -13,24 +13,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
-/**
- * The gd API exported to the legacy api
- */
+#include <functional>
+#include <list>
+
 namespace bluetooth {
 namespace shim {
 
-struct IDiscoverability {
-  virtual void StartGeneralDiscoverability() = 0;
-  virtual void StartLimitedDiscoverability() = 0;
-  virtual void StopDiscoverability() = 0;
+using DumpsysFunction = std::function<void(int fd)>;
 
-  virtual bool IsGeneralDiscoverabilityEnabled() const = 0;
-  virtual bool IsLimitedDiscoverabilityEnabled() const = 0;
+/**
+ * Entrypoint from legacy stack to provide dumpsys functionality
+ * for both the legacy shim and the Gabeldorsche stack.
+ */
+void Dump(int fd, const char** args);
 
-  virtual ~IDiscoverability() {}
-};
+/**
+ * Dumpsys access for legacy shim modules.
+ */
+void RegisterDumpsysFunction(const void* token, DumpsysFunction func);
+void UnregisterDumpsysFunction(const void* token);
 
 }  // namespace shim
 }  // namespace bluetooth
diff --git a/main/shim/entry.cc b/main/shim/entry.cc
index 77a5fc0..46c18ff 100644
--- a/main/shim/entry.cc
+++ b/main/shim/entry.cc
@@ -14,58 +14,108 @@
  * limitations under the License.
  */
 
+#include "gd/hci/controller.h"
+#include "gd/hci/hci_layer.h"
+#include "gd/hci/le_advertising_manager.h"
+#include "gd/hci/le_scanning_manager.h"
+#include "gd/neighbor/connectability.h"
+#include "gd/neighbor/discoverability.h"
+#include "gd/neighbor/inquiry.h"
+#include "gd/neighbor/name.h"
+#include "gd/neighbor/page.h"
+#include "gd/os/handler.h"
+#include "gd/security/security_module.h"
+#include "gd/shim/dumpsys.h"
+#include "gd/shim/l2cap.h"
+#include "gd/storage/storage_module.h"
+
+#include "hci/acl_manager.h"
+
 #include "main/shim/entry.h"
-#include "gd/shim/only_include_this_file_into_legacy_stack___ever.h"
-#include "osi/include/future.h"
+#include "main/shim/stack.h"
 
-using bluetooth::shim::GetGabeldorscheStack;
+namespace bluetooth {
+namespace shim {
 
-future_t* bluetooth::shim::StartGabeldorscheStack() {
-  GetGabeldorscheStack()->Start();
-  return (future_t*)nullptr;
+os::Handler* GetGdShimHandler() { return GetDumpsys()->GetGdShimHandler(); }
+
+hci::LeAdvertisingManager* GetAdvertising() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<hci::LeAdvertisingManager>();
 }
 
-future_t* bluetooth::shim::StopGabeldorscheStack() {
-  GetGabeldorscheStack()->Stop();
-  return (future_t*)nullptr;
+hci::Controller* GetController() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<hci::Controller>();
 }
 
-bluetooth::shim::IAdvertising* bluetooth::shim::GetAdvertising() {
-  return GetGabeldorscheStack()->GetAdvertising();
+neighbor::ConnectabilityModule* GetConnectability() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<neighbor::ConnectabilityModule>();
 }
 
-bluetooth::shim::IController* bluetooth::shim::GetController() {
-  return GetGabeldorscheStack()->GetController();
+neighbor::DiscoverabilityModule* GetDiscoverability() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<neighbor::DiscoverabilityModule>();
 }
 
-bluetooth::shim::IConnectability* bluetooth::shim::GetConnectability() {
-  return GetGabeldorscheStack()->GetConnectability();
+Dumpsys* GetDumpsys() {
+  return Stack::GetInstance()->GetStackManager()->GetInstance<Dumpsys>();
 }
 
-bluetooth::shim::IDiscoverability* bluetooth::shim::GetDiscoverability() {
-  return GetGabeldorscheStack()->GetDiscoverability();
+neighbor::InquiryModule* GetInquiry() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<neighbor::InquiryModule>();
 }
 
-bluetooth::shim::IInquiry* bluetooth::shim::GetInquiry() {
-  return GetGabeldorscheStack()->GetInquiry();
+hci::HciLayer* GetHciLayer() {
+  return Stack::GetInstance()->GetStackManager()->GetInstance<hci::HciLayer>();
 }
 
-bluetooth::shim::IHciLayer* bluetooth::shim::GetHciLayer() {
-  return GetGabeldorscheStack()->GetHciLayer();
+L2cap* GetL2cap() {
+  return Stack::GetInstance()->GetStackManager()->GetInstance<L2cap>();
 }
 
-bluetooth::shim::IL2cap* bluetooth::shim::GetL2cap() {
-  return GetGabeldorscheStack()->GetL2cap();
+neighbor::NameModule* GetName() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<neighbor::NameModule>();
 }
 
-bluetooth::shim::IName* bluetooth::shim::GetName() {
-  return GetGabeldorscheStack()->GetName();
+neighbor::PageModule* GetPage() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<neighbor::PageModule>();
 }
 
-bluetooth::shim::IPage* bluetooth::shim::GetPage() {
-  return GetGabeldorscheStack()->GetPage();
+hci::LeScanningManager* GetScanning() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<hci::LeScanningManager>();
 }
 
-bluetooth::shim::IScanning* bluetooth::shim::GetScanning() {
-  return GetGabeldorscheStack()->GetScanning();
+security::SecurityModule* GetSecurityModule() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<security::SecurityModule>();
 }
+
+storage::StorageModule* GetStorage() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<storage::StorageModule>();
+}
+
+hci::AclManager* GetAclManager() {
+  return Stack::GetInstance()
+      ->GetStackManager()
+      ->GetInstance<hci::AclManager>();
+}
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/entry.h b/main/shim/entry.h
index 56f7404..4584164 100644
--- a/main/shim/entry.h
+++ b/main/shim/entry.h
@@ -33,21 +33,50 @@
 #include "osi/include/future.h"
 
 namespace bluetooth {
+namespace os {
+class Handler;
+}
+namespace neighbor {
+class ConnectabilityModule;
+class DiscoverabilityModule;
+class InquiryModule;
+class NameModule;
+class PageModule;
+}
+namespace hci {
+class Controller;
+class HciLayer;
+class AclManager;
+class LeAdvertisingManager;
+class LeScanningManager;
+}
+
+namespace security {
+class SecurityModule;
+}
+namespace storage {
+class StorageModule;
+}
+
 namespace shim {
 
-future_t* StartGabeldorscheStack();
-future_t* StopGabeldorscheStack();
-
-bluetooth::shim::IAdvertising* GetAdvertising();
-bluetooth::shim::IController* GetController();
-bluetooth::shim::IDiscoverability* GetDiscoverability();
-bluetooth::shim::IConnectability* GetConnectability();
-bluetooth::shim::IInquiry* GetInquiry();
-bluetooth::shim::IHciLayer* GetHciLayer();
-bluetooth::shim::IL2cap* GetL2cap();
-bluetooth::shim::IName* GetName();
-bluetooth::shim::IPage* GetPage();
-bluetooth::shim::IScanning* GetScanning();
+/* This returns a handler that might be used in shim to receive callbacks from
+ * within the stack. */
+os::Handler* GetGdShimHandler();
+hci::LeAdvertisingManager* GetAdvertising();
+bluetooth::hci::Controller* GetController();
+neighbor::DiscoverabilityModule* GetDiscoverability();
+neighbor::ConnectabilityModule* GetConnectability();
+Dumpsys* GetDumpsys();
+neighbor::InquiryModule* GetInquiry();
+hci::HciLayer* GetHciLayer();
+L2cap* GetL2cap();
+neighbor::NameModule* GetName();
+neighbor::PageModule* GetPage();
+hci::LeScanningManager* GetScanning();
+bluetooth::security::SecurityModule* GetSecurityModule();
+storage::StorageModule* GetStorage();
+hci::AclManager* GetAclManager();
 
 }  // namespace shim
 }  // namespace bluetooth
diff --git a/main/shim/entry_for_test.cc b/main/shim/entry_for_test.cc
deleted file mode 100644
index 26b9b22..0000000
--- a/main/shim/entry_for_test.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "gd/shim/only_include_this_file_into_legacy_stack___ever.h"
-#include "main/shim/entry.h"
-
-/**
- * Entrypoints for stubbed bluetooth test library
- *
- * Gd stack is not supported using legacy test modes
- */
-bluetooth::shim::IStack* bluetooth::shim::GetGabeldorscheStack() {
-  return (bluetooth::shim::IStack*)nullptr;
-}
diff --git a/main/shim/hci_layer.cc b/main/shim/hci_layer.cc
index ba21cae..b9953a5 100644
--- a/main/shim/hci_layer.cc
+++ b/main/shim/hci_layer.cc
@@ -22,10 +22,12 @@
 #include <cstdint>
 
 #include "btcore/include/module.h"
+#include "hci/hci_layer.h"
 #include "main/shim/hci_layer.h"
 #include "main/shim/shim.h"
 #include "osi/include/allocator.h"
 #include "osi/include/future.h"
+#include "packet/raw_builder.h"
 #include "stack/include/bt_types.h"
 
 /**
@@ -35,79 +37,102 @@
  * Upon completion a token for a corresponding command transmit.
  * request is returned from the Gd layer.
  */
-using CommandCallbackData = struct {
-  void* context;
-  command_complete_cb complete_callback;
-  command_status_cb status_callback;
-};
+using CommandCallbackData = struct { void* context; };
 
 constexpr size_t kBtHdrSize = sizeof(BT_HDR);
 constexpr size_t kCommandLengthSize = sizeof(uint8_t);
 constexpr size_t kCommandOpcodeSize = sizeof(uint16_t);
 
-static hci_t interface;
 static base::Callback<void(const base::Location&, BT_HDR*)> send_data_upwards;
 
-static future_t* hci_module_shut_down(void);
-static future_t* hci_module_start_up(void);
-
-static void OnCommandComplete(uint16_t command_op_code,
-                              std::vector<const uint8_t> data,
-                              const void* token) {
-  BT_HDR* response = static_cast<BT_HDR*>(osi_calloc(data.size() + kBtHdrSize));
-  std::copy(data.begin(), data.end(), response->data);
-  response->len = data.size();
-
-  const CommandCallbackData* command_callback_data =
-      static_cast<const CommandCallbackData*>(token);
-  CHECK(command_callback_data->complete_callback != nullptr);
-
-  command_callback_data->complete_callback(response,
-                                           command_callback_data->context);
-  delete command_callback_data;
+namespace {
+bool IsCommandStatusOpcode(bluetooth::hci::OpCode op_code) {
+  switch (op_code) {
+    case bluetooth::hci::OpCode::INQUIRY:
+    case bluetooth::hci::OpCode::CREATE_CONNECTION:
+    case bluetooth::hci::OpCode::DISCONNECT:
+    case bluetooth::hci::OpCode::ACCEPT_CONNECTION_REQUEST:
+    case bluetooth::hci::OpCode::REJECT_CONNECTION_REQUEST:
+    case bluetooth::hci::OpCode::CHANGE_CONNECTION_PACKET_TYPE:
+    case bluetooth::hci::OpCode::AUTHENTICATION_REQUESTED:
+    case bluetooth::hci::OpCode::SET_CONNECTION_ENCRYPTION:
+    case bluetooth::hci::OpCode::CHANGE_CONNECTION_LINK_KEY:
+    case bluetooth::hci::OpCode::MASTER_LINK_KEY:
+    case bluetooth::hci::OpCode::REMOTE_NAME_REQUEST:
+    case bluetooth::hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES:
+    case bluetooth::hci::OpCode::READ_REMOTE_EXTENDED_FEATURES:
+    case bluetooth::hci::OpCode::READ_REMOTE_VERSION_INFORMATION:
+    case bluetooth::hci::OpCode::READ_CLOCK_OFFSET:
+    case bluetooth::hci::OpCode::SETUP_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::ACCEPT_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::REJECT_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::ENHANCED_SETUP_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::HOLD_MODE:
+    case bluetooth::hci::OpCode::SNIFF_MODE:
+    case bluetooth::hci::OpCode::EXIT_SNIFF_MODE:
+    case bluetooth::hci::OpCode::QOS_SETUP:
+    case bluetooth::hci::OpCode::SWITCH_ROLE:
+    case bluetooth::hci::OpCode::FLOW_SPECIFICATION:
+    case bluetooth::hci::OpCode::REFRESH_ENCRYPTION_KEY:
+    case bluetooth::hci::OpCode::LE_CREATE_CONNECTION:
+    case bluetooth::hci::OpCode::LE_CONNECTION_UPDATE:
+    case bluetooth::hci::OpCode::LE_READ_REMOTE_FEATURES:
+    case bluetooth::hci::OpCode::LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND:
+    case bluetooth::hci::OpCode::LE_GENERATE_DHKEY_COMMAND:
+    case bluetooth::hci::OpCode::LE_SET_PHY:
+    case bluetooth::hci::OpCode::LE_EXTENDED_CREATE_CONNECTION:
+    case bluetooth::hci::OpCode::LE_PERIODIC_ADVERTISING_CREATE_SYNC:
+      return true;
+    default:
+      return false;
+  }
 }
 
-static void OnCommandStatus(uint16_t command_op_code,
-                            std::vector<const uint8_t> data, const void* token,
-                            uint8_t status) {
-  BT_HDR* response = static_cast<BT_HDR*>(osi_calloc(data.size() + kBtHdrSize));
-  std::copy(data.begin(), data.end(), response->data);
-  response->len = data.size();
+std::unique_ptr<bluetooth::packet::RawBuilder> MakeUniquePacket(
+    const uint8_t* data, size_t len) {
+  bluetooth::packet::RawBuilder builder;
+  std::vector<uint8_t> bytes(data, data + len);
 
-  const CommandCallbackData* command_callback_data =
-      static_cast<const CommandCallbackData*>(token);
-  CHECK(command_callback_data->status_callback != nullptr);
+  auto payload = std::make_unique<bluetooth::packet::RawBuilder>();
+  payload->AddOctets(bytes);
 
-  command_callback_data->status_callback(status, response,
-                                         command_callback_data->context);
-  delete command_callback_data;
+  return payload;
 }
-
-EXPORT_SYMBOL extern const module_t gd_hci_module = {
-    .name = GD_HCI_MODULE,
-    .init = nullptr,
-    .start_up = hci_module_start_up,
-    .shut_down = hci_module_shut_down,
-    .clean_up = nullptr,
-    .dependencies = {GD_SHIM_MODULE, nullptr}};
-
-static future_t* hci_module_start_up(void) {
-  bluetooth::shim::GetHciLayer()->RegisterCommandComplete(OnCommandComplete);
-  bluetooth::shim::GetHciLayer()->RegisterCommandStatus(OnCommandStatus);
-  return nullptr;
-}
-
-static future_t* hci_module_shut_down(void) {
-  bluetooth::shim::GetHciLayer()->UnregisterCommandComplete();
-  bluetooth::shim::GetHciLayer()->UnregisterCommandStatus();
-  return nullptr;
-}
+}  // namespace
 
 static void set_data_cb(
     base::Callback<void(const base::Location&, BT_HDR*)> send_data_cb) {
   send_data_upwards = std::move(send_data_cb);
 }
 
+void OnTransmitPacketCommandComplete(command_complete_cb complete_callback,
+                                     void* context,
+                                     bluetooth::hci::CommandCompleteView view) {
+  std::vector<const uint8_t> data(view.begin(), view.end());
+
+  BT_HDR* response = static_cast<BT_HDR*>(osi_calloc(data.size() + kBtHdrSize));
+  std::copy(data.begin(), data.end(), response->data);
+  response->len = data.size();
+
+  complete_callback(response, context);
+}
+
+void OnTransmitPacketStatus(command_status_cb status_callback, void* context,
+                            bluetooth::hci::CommandStatusView view) {
+  std::vector<const uint8_t> data(view.begin(), view.end());
+
+  BT_HDR* response = static_cast<BT_HDR*>(osi_calloc(data.size() + kBtHdrSize));
+  std::copy(data.begin(), data.end(), response->data);
+  response->len = data.size();
+
+  uint8_t status = static_cast<uint8_t>(view.GetStatus());
+  status_callback(status, response, context);
+}
+
+using bluetooth::common::BindOnce;
+using bluetooth::common::Unretained;
+
 static void transmit_command(BT_HDR* command,
                              command_complete_cb complete_callback,
                              command_status_cb status_callback, void* context) {
@@ -123,24 +148,29 @@
   data += (kCommandOpcodeSize + kCommandLengthSize);
   len -= (kCommandOpcodeSize + kCommandLengthSize);
 
-  const CommandCallbackData* command_callback_data = new CommandCallbackData{
-      context,
-      complete_callback,
-      status_callback,
-  };
-  bluetooth::shim::GetHciLayer()->TransmitCommand(
-      command_op_code, const_cast<const uint8_t*>(data), len,
-      static_cast<const void*>(command_callback_data));
+  const bluetooth::hci::OpCode op_code =
+      static_cast<const bluetooth::hci::OpCode>(command_op_code);
+
+  auto payload = MakeUniquePacket(data, len);
+  auto packet =
+      bluetooth::hci::CommandPacketBuilder::Create(op_code, std::move(payload));
+
+  if (IsCommandStatusOpcode(op_code)) {
+    bluetooth::shim::GetHciLayer()->EnqueueCommand(
+        std::move(packet),
+        bluetooth::shim::GetGdShimHandler()->BindOnce(
+            OnTransmitPacketStatus, status_callback, context));
+  } else {
+    bluetooth::shim::GetHciLayer()->EnqueueCommand(
+        std::move(packet),
+        bluetooth::shim::GetGdShimHandler()->BindOnce(
+            OnTransmitPacketCommandComplete, complete_callback, context));
+  }
 }
 
-const hci_t* bluetooth::shim::hci_layer_get_interface() {
-  static bool loaded = false;
-  if (!loaded) {
-    loaded = true;
-    interface.set_data_cb = set_data_cb;
-    interface.transmit_command = transmit_command;
-    interface.transmit_command_futured = nullptr;
-    interface.transmit_downward = nullptr;
-  }
-  return &interface;
-}
+static hci_t interface = {.set_data_cb = set_data_cb,
+                          .transmit_command = transmit_command,
+                          .transmit_command_futured = nullptr,
+                          .transmit_downward = nullptr};
+
+const hci_t* bluetooth::shim::hci_layer_get_interface() { return &interface; }
diff --git a/main/shim/hci_layer.h b/main/shim/hci_layer.h
index 6b0bbcc..284c6a9 100644
--- a/main/shim/hci_layer.h
+++ b/main/shim/hci_layer.h
@@ -21,8 +21,6 @@
 
 #include "hci/include/hci_layer.h"
 
-static const char GD_HCI_MODULE[] = "gd_hci_module";
-
 namespace bluetooth {
 namespace shim {
 
diff --git a/main/shim/helpers.h b/main/shim/helpers.h
new file mode 100644
index 0000000..a2fa33f
--- /dev/null
+++ b/main/shim/helpers.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include "hci/address_with_type.h"
+
+#include "stack/include/bt_types.h"
+
+namespace bluetooth {
+
+inline RawAddress ToRawAddress(const hci::Address& address) {
+  RawAddress ret;
+  ret.address[0] = address.address[5];
+  ret.address[1] = address.address[4];
+  ret.address[2] = address.address[3];
+  ret.address[3] = address.address[2];
+  ret.address[4] = address.address[1];
+  ret.address[5] = address.address[0];
+  return ret;
+}
+
+inline hci::Address ToGdAddress(const RawAddress& address) {
+  hci::Address ret;
+  ret.address[0] = address.address[5];
+  ret.address[1] = address.address[4];
+  ret.address[2] = address.address[3];
+  ret.address[3] = address.address[2];
+  ret.address[4] = address.address[1];
+  ret.address[5] = address.address[0];
+  return ret;
+}
+
+inline hci::AddressWithType ToAddressWithType(const RawAddress& legacy_address,
+                                       tBLE_ADDR_TYPE legacy_type) {
+  hci::Address address = ToGdAddress(legacy_address);
+
+  hci::AddressType type;
+  if (legacy_type == BLE_ADDR_PUBLIC)
+    type = hci::AddressType::PUBLIC_DEVICE_ADDRESS;
+  else if (legacy_type == BLE_ADDR_RANDOM)
+    type = hci::AddressType::RANDOM_DEVICE_ADDRESS;
+  else if (legacy_type == BLE_ADDR_PUBLIC_ID)
+    type = hci::AddressType::PUBLIC_IDENTITY_ADDRESS;
+  else if (legacy_type == BLE_ADDR_RANDOM_ID)
+    type = hci::AddressType::RANDOM_IDENTITY_ADDRESS;
+  else {
+    LOG_ALWAYS_FATAL("Bad address type %02x", legacy_type);
+    return hci::AddressWithType{address,
+                                hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  }
+
+  return hci::AddressWithType{address, type};
+}
+}  // namespace bluetooth
diff --git a/main/shim/l2c_api.cc b/main/shim/l2c_api.cc
index 07e864f..fae6064 100644
--- a/main/shim/l2c_api.cc
+++ b/main/shim/l2c_api.cc
@@ -21,16 +21,19 @@
 #include "main/shim/shim.h"
 #include "osi/include/log.h"
 
-static bluetooth::legacy::shim::L2cap shim_l2cap;
+static bluetooth::shim::legacy::L2cap shim_l2cap;
 
 /**
  * Classic Service Registration APIs
  */
 uint16_t bluetooth::shim::L2CA_Register(uint16_t client_psm,
                                         tL2CAP_APPL_INFO* callbacks,
-                                        bool enable_snoop) {
+                                        bool enable_snoop,
+                                        tL2CAP_ERTM_INFO* p_ertm_info) {
+  CHECK(callbacks != nullptr);
+
   if (L2C_INVALID_PSM(client_psm)) {
-    LOG_ERROR(LOG_TAG, "%s Invalid classic psm:%hd", __func__, client_psm);
+    LOG_ERROR("%s Invalid classic psm:%hd", __func__, client_psm);
     return 0;
   }
 
@@ -38,8 +41,7 @@
       (callbacks->pL2CA_ConfigInd_Cb == nullptr) ||
       (callbacks->pL2CA_DataInd_Cb == nullptr) ||
       (callbacks->pL2CA_DisconnectInd_Cb == nullptr)) {
-    LOG_ERROR(LOG_TAG, "%s Invalid classic callbacks psm:%hd", __func__,
-              client_psm);
+    LOG_ERROR("%s Invalid classic callbacks psm:%hd", __func__, client_psm);
     return 0;
   }
 
@@ -51,61 +53,40 @@
                                                    is_outgoing_connection_only);
 
   if (shim_l2cap.Classic().IsPsmRegistered(psm)) {
-    LOG_ERROR(LOG_TAG, "%s Already registered classic client_psm:%hd psm:%hd",
-              __func__, client_psm, psm);
+    LOG_ERROR("%s Already registered classic client_psm:%hd psm:%hd", __func__,
+              client_psm, psm);
     return 0;
   }
-  shim_l2cap.Classic().RegisterPsm(psm, callbacks);
+  LOG_INFO("%s classic client_psm:%hd psm:%hd", __func__, client_psm, psm);
 
-  LOG_INFO(LOG_TAG, "%s classic client_psm:%hd psm:%hd", __func__, client_psm,
-           psm);
-
-  shim_l2cap.RegisterService(psm, callbacks, enable_snoop);
-
-  return client_psm;
+  return shim_l2cap.RegisterService(psm, callbacks, enable_snoop, p_ertm_info);
 }
 
 void bluetooth::shim::L2CA_Deregister(uint16_t client_psm) {
   if (L2C_INVALID_PSM(client_psm)) {
-    LOG_ERROR(LOG_TAG, "%s Invalid classic client_psm:%hd", __func__,
-              client_psm);
+    LOG_ERROR("%s Invalid classic client_psm:%hd", __func__, client_psm);
     return;
   }
   uint16_t psm = shim_l2cap.ConvertClientToRealPsm(client_psm);
 
-  if (!shim_l2cap.Classic().IsPsmRegistered(psm)) {
-    LOG_ERROR(LOG_TAG,
-              "%s Not previously registered classic client_psm:%hd psm:%hd",
-              __func__, client_psm, psm);
-    return;
-  }
-  shim_l2cap.Classic().UnregisterPsm(psm);
+  shim_l2cap.UnregisterService(psm);
   shim_l2cap.RemoveClientPsm(psm);
 }
 
 uint16_t bluetooth::shim::L2CA_AllocatePSM(void) {
-  uint16_t psm = shim_l2cap.GetNextDynamicClassicPsm();
-  shim_l2cap.Classic().AllocatePsm(psm);
-  return psm;
+  return shim_l2cap.GetNextDynamicClassicPsm();
 }
 
 uint16_t bluetooth::shim::L2CA_AllocateLePSM(void) {
-  uint16_t psm = shim_l2cap.GetNextDynamicLePsm();
-  shim_l2cap.Le().AllocatePsm(psm);
-  return psm;
+  return shim_l2cap.GetNextDynamicLePsm();
 }
 
 void bluetooth::shim::L2CA_FreeLePSM(uint16_t psm) {
-  if (!shim_l2cap.Le().IsPsmAllocated(psm)) {
-    LOG_ERROR(LOG_TAG, "%s Not previously allocated le psm:%hd", __func__, psm);
-    return;
-  }
   if (!shim_l2cap.Le().IsPsmRegistered(psm)) {
-    LOG_ERROR(LOG_TAG, "%s Must deregister psm before deallocation psm:%hd",
-              __func__, psm);
+    LOG_ERROR("%s Not previously registered le psm:%hd", __func__, psm);
     return;
   }
-  shim_l2cap.Le().DeallocatePsm(psm);
+  shim_l2cap.Le().UnregisterPsm(psm);
 }
 
 /**
@@ -114,33 +95,27 @@
 uint16_t bluetooth::shim::L2CA_ErtmConnectReq(uint16_t psm,
                                               const RawAddress& raw_address,
                                               tL2CAP_ERTM_INFO* p_ertm_info) {
-  CHECK(p_ertm_info == nullptr)
-      << "UNIMPLEMENTED set enhanced retransmission mode config";
   return shim_l2cap.CreateConnection(psm, raw_address);
 }
 
 uint16_t bluetooth::shim::L2CA_ConnectReq(uint16_t psm,
                                           const RawAddress& raw_address) {
-  return bluetooth::shim::L2CA_ErtmConnectReq(psm, raw_address, nullptr);
+  return shim_l2cap.CreateConnection(psm, raw_address);
 }
 
 bool bluetooth::shim::L2CA_ErtmConnectRsp(const RawAddress& p_bd_addr,
                                           uint8_t id, uint16_t lcid,
                                           uint16_t result, uint16_t status,
                                           tL2CAP_ERTM_INFO* p_ertm_info) {
-  LOG_INFO(LOG_TAG,
-           "UNIMPLEMENTED %s addr:%s id:%hhd lcid:%hd result:%hd status:%hd "
-           "p_ertm_info:%p",
-           __func__, p_bd_addr.ToString().c_str(), id, lcid, result, status,
-           p_ertm_info);
-  return false;
+  return shim_l2cap.ConnectResponse(p_bd_addr, id, lcid, result, status,
+                                    p_ertm_info);
 }
 
 bool bluetooth::shim::L2CA_ConnectRsp(const RawAddress& p_bd_addr, uint8_t id,
                                       uint16_t lcid, uint16_t result,
                                       uint16_t status) {
   return bluetooth::shim::L2CA_ErtmConnectRsp(p_bd_addr, id, lcid, result,
-                                              status, NULL);
+                                              status, nullptr);
 }
 
 bool bluetooth::shim::L2CA_ConfigReq(uint16_t cid, tL2CAP_CFG_INFO* cfg_info) {
@@ -164,19 +139,18 @@
  */
 uint16_t bluetooth::shim::L2CA_RegisterLECoc(uint16_t psm,
                                              tL2CAP_APPL_INFO* callbacks) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s psm:%hd callbacks:%p", __func__, psm,
-           callbacks);
+  LOG_INFO("UNIMPLEMENTED %s psm:%hd callbacks:%p", __func__, psm, callbacks);
   return 0;
 }
 
 void bluetooth::shim::L2CA_DeregisterLECoc(uint16_t psm) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s psm:%hd", __func__, psm);
+  LOG_INFO("UNIMPLEMENTED %s psm:%hd", __func__, psm);
 }
 
 uint16_t bluetooth::shim::L2CA_ConnectLECocReq(uint16_t psm,
                                                const RawAddress& p_bd_addr,
                                                tL2CAP_LE_CFG_INFO* p_cfg) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s psm:%hd addr:%s p_cfg:%p", __func__, psm,
+  LOG_INFO("UNIMPLEMENTED %s psm:%hd addr:%s p_cfg:%p", __func__, psm,
            p_bd_addr.ToString().c_str(), p_cfg);
   return 0;
 }
@@ -185,18 +159,16 @@
                                            uint8_t id, uint16_t lcid,
                                            uint16_t result, uint16_t status,
                                            tL2CAP_LE_CFG_INFO* p_cfg) {
-  LOG_INFO(LOG_TAG,
-           "UNIMPLEMENTED %s addr:%s id:%hhd lcid:%hd result:%hd status:%hd "
-           "p_cfg:%p",
-           __func__, p_bd_addr.ToString().c_str(), id, lcid, result, status,
-           p_cfg);
+  LOG_INFO(
+      "UNIMPLEMENTED %s addr:%s id:%hhd lcid:%hd result:%hd status:%hd "
+      "p_cfg:%p",
+      __func__, p_bd_addr.ToString().c_str(), id, lcid, result, status, p_cfg);
   return false;
 }
 
 bool bluetooth::shim::L2CA_GetPeerLECocConfig(uint16_t lcid,
                                               tL2CAP_LE_CFG_INFO* peer_cfg) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s lcid:%hd peer_cfg:%p", __func__, lcid,
-           peer_cfg);
+  LOG_INFO("UNIMPLEMENTED %s lcid:%hd peer_cfg:%p", __func__, lcid, peer_cfg);
   return false;
 }
 
@@ -205,7 +177,7 @@
  */
 bool bluetooth::shim::L2CA_SetConnectionCallbacks(
     uint16_t cid, const tL2CAP_APPL_INFO* callbacks) {
-  LOG_INFO(LOG_TAG, "Unsupported API %s", __func__);
+  LOG_INFO("Unsupported API %s", __func__);
   return false;
 }
 
@@ -218,7 +190,7 @@
  * L2cap Layer APIs
  */
 uint8_t bluetooth::shim::L2CA_SetDesireRole(uint8_t new_role) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return 0;
 }
 
@@ -228,146 +200,99 @@
 bool bluetooth::shim::L2CA_SetIdleTimeoutByBdAddr(const RawAddress& bd_addr,
                                                   uint16_t timeout,
                                                   tBT_TRANSPORT transport) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
-uint16_t bluetooth::shim::L2CA_LocalLoopbackReq(uint16_t psm, uint16_t handle,
-                                                const RawAddress& p_bd_addr) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  return 0;
-}
-
 bool bluetooth::shim::L2CA_SetAclPriority(const RawAddress& bd_addr,
                                           uint8_t priority) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 bool bluetooth::shim::L2CA_SetFlushTimeout(const RawAddress& bd_addr,
                                            uint16_t flush_tout) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 bool bluetooth::shim::L2CA_GetPeerFeatures(const RawAddress& bd_addr,
                                            uint32_t* p_ext_feat,
                                            uint8_t* p_chnl_mask) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  return false;
-}
-
-bool bluetooth::shim::L2CA_GetBDAddrbyHandle(uint16_t handle,
-                                             RawAddress& bd_addr) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 /**
- * Fixed Channel APIs
+ * Fixed Channel APIs. Note: Classic fixed channel (connectionless and BR SMP)
+ * is not supported
  */
 bool bluetooth::shim::L2CA_RegisterFixedChannel(uint16_t fixed_cid,
                                                 tL2CAP_FIXED_CHNL_REG* p_freg) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 bool bluetooth::shim::L2CA_ConnectFixedChnl(uint16_t fixed_cid,
                                             const RawAddress& rem_bda) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 bool bluetooth::shim::L2CA_ConnectFixedChnl(uint16_t fixed_cid,
                                             const RawAddress& rem_bda,
                                             uint8_t initiating_phys) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 uint16_t bluetooth::shim::L2CA_SendFixedChnlData(uint16_t fixed_cid,
                                                  const RawAddress& rem_bda,
                                                  BT_HDR* p_buf) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return 0;
 }
 
 bool bluetooth::shim::L2CA_RemoveFixedChnl(uint16_t fixed_cid,
                                            const RawAddress& rem_bda) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  return false;
-}
-
-/**
- * Channel Configuration API
- */
-bool bluetooth::shim::L2CA_GetCurrentConfig(
-    uint16_t lcid, tL2CAP_CFG_INFO** pp_our_cfg,
-    tL2CAP_CH_CFG_BITS* p_our_cfg_bits, tL2CAP_CFG_INFO** pp_peer_cfg,
-    tL2CAP_CH_CFG_BITS* p_peer_cfg_bits) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  return false;
-}
-
-bool bluetooth::shim::L2CA_GetConnectionConfig(uint16_t lcid, uint16_t* mtu,
-                                               uint16_t* rcid,
-                                               uint16_t* handle) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 /**
  * Channel hygiene APIs
  */
-bool bluetooth::shim::L2CA_GetIdentifiers(uint16_t lcid, uint16_t* rcid,
-                                          uint16_t* handle) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  return false;
+bool bluetooth::shim::L2CA_GetRemoteCid(uint16_t lcid, uint16_t* rcid) {
+  return shim_l2cap.GetRemoteCid(lcid, rcid);
 }
 
 bool bluetooth::shim::L2CA_SetIdleTimeout(uint16_t cid, uint16_t timeout,
                                           bool is_global) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  return false;
-}
-
-bool bluetooth::shim::L2CA_FlowControl(uint16_t cid, bool data_enabled) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  return false;
-}
-
-bool bluetooth::shim::L2CA_SendTestSFrame(uint16_t cid, uint8_t sup_type,
-                                          uint8_t back_track) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 bool bluetooth::shim::L2CA_SetTxPriority(uint16_t cid,
                                          tL2CAP_CHNL_PRIORITY priority) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
-uint8_t bluetooth::shim::L2CA_GetChnlFcrMode(uint16_t lcid) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
-  return 0;
-}
-
 bool bluetooth::shim::L2CA_SetFixedChannelTout(const RawAddress& rem_bda,
                                                uint16_t fixed_cid,
                                                uint16_t idle_tout) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 bool bluetooth::shim::L2CA_SetChnlFlushability(uint16_t cid,
                                                bool is_flushable) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return false;
 }
 
 uint16_t bluetooth::shim::L2CA_FlushChannel(uint16_t lcid,
                                             uint16_t num_to_flush) {
-  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  LOG_INFO("UNIMPLEMENTED %s", __func__);
   return 0;
 }
diff --git a/main/shim/l2c_api.h b/main/shim/l2c_api.h
index f5e5155..f5fbfa0 100644
--- a/main/shim/l2c_api.h
+++ b/main/shim/l2c_api.h
@@ -40,7 +40,7 @@
  *
  ******************************************************************************/
 uint16_t L2CA_Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                       bool enable_snoop);
+                       bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info);
 
 /*******************************************************************************
  *
@@ -288,11 +288,10 @@
 uint8_t L2CA_DataWrite(uint16_t cid, BT_HDR* p_data);
 
 // Given a local channel identifier, |lcid|, this function returns the bound
-// remote channel identifier, |rcid|, and the ACL link handle, |handle|. If
+// remote channel identifier, |rcid|. If
 // |lcid| is not known or is invalid, this function returns false and does not
-// modify the values pointed at by |rcid| and |handle|. |rcid| and |handle| may
-// be NULL.
-bool L2CA_GetIdentifiers(uint16_t lcid, uint16_t* rcid, uint16_t* handle);
+// modify the value pointed at by |rcid|. |rcid| may be NULL.
+bool L2CA_GetRemoteCid(uint16_t lcid, uint16_t* rcid);
 
 /*******************************************************************************
  *
@@ -366,18 +365,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_LocalLoopbackReq
- *
- * Description  This function sets up a CID for local loopback
- *
- * Returns      CID of 0 if none.
- *
- ******************************************************************************/
-uint16_t L2CA_LocalLoopbackReq(uint16_t psm, uint16_t handle,
-                               const RawAddress& p_bd_addr);
-
-/*******************************************************************************
- *
  * Function     L2CA_FlushChannel
  *
  * Description  This function flushes none, some or all buffers queued up
@@ -407,30 +394,6 @@
 
 /*******************************************************************************
  *
- * Function         L2CA_FlowControl
- *
- * Description      Higher layers call this function to flow control a channel.
- *
- *                  data_enabled - true data flows, false data is stopped
- *
- * Returns          true if valid channel, else false
- *
- ******************************************************************************/
-bool L2CA_FlowControl(uint16_t cid, bool data_enabled);
-
-/*******************************************************************************
- *
- * Function         L2CA_SendTestSFrame
- *
- * Description      Higher layers call this function to send a test S-frame.
- *
- * Returns          true if valid Channel, else false
- *
- ******************************************************************************/
-bool L2CA_SendTestSFrame(uint16_t cid, uint8_t sup_type, uint8_t back_track);
-
-/*******************************************************************************
- *
  * Function         L2CA_SetTxPriority
  *
  * Description      Sets the transmission priority for a channel. (FCR Mode)
@@ -440,8 +403,6 @@
  ******************************************************************************/
 bool L2CA_SetTxPriority(uint16_t cid, tL2CAP_CHNL_PRIORITY priority);
 
-typedef void(tL2CA_RESERVE_CMPL_CBACK)(void);
-
 /*******************************************************************************
  *
  * Function         L2CA_SetFlushTimeout
@@ -497,33 +458,6 @@
 
 /*******************************************************************************
  *
- *  Function         L2CA_GetBDAddrbyHandle
- *
- *  Description      Get BD address for the given HCI handle
- *
- *  Parameters:      HCI handle
- *                   BD address of the peer
- *
- *  Return value:    true if found lcb for the given handle, false otherwise
- *
- ******************************************************************************/
-bool L2CA_GetBDAddrbyHandle(uint16_t handle, RawAddress& bd_addr);
-
-/*******************************************************************************
- *
- *  Function         L2CA_GetChnlFcrMode
- *
- *  Description      Get the channel FCR mode
- *
- *  Parameters:      Local CID
- *
- *  Return value:    Channel mode
- *
- ******************************************************************************/
-uint8_t L2CA_GetChnlFcrMode(uint16_t lcid);
-
-/*******************************************************************************
- *
  *  Function        L2CA_RegisterFixedChannel
  *
  *  Description     Register a fixed channel.
@@ -607,37 +541,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_GetCurrentConfig
- *
- * Description  This function returns configurations of L2CAP channel
- *              pp_our_cfg : pointer of our saved configuration options
- *              p_our_cfg_bits : valid config in bitmap
- *              pp_peer_cfg: pointer of peer's saved configuration options
- *              p_peer_cfg_bits : valid config in bitmap
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-bool L2CA_GetCurrentConfig(uint16_t lcid, tL2CAP_CFG_INFO** pp_our_cfg,
-                           tL2CAP_CH_CFG_BITS* p_our_cfg_bits,
-                           tL2CAP_CFG_INFO** pp_peer_cfg,
-                           tL2CAP_CH_CFG_BITS* p_peer_cfg_bits);
-
-/*******************************************************************************
- *
- * Function     L2CA_GetConnectionConfig
- *
- * Description  This function polulates the mtu, remote cid & lm_handle for
- *              a given local L2CAP channel
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-bool L2CA_GetConnectionConfig(uint16_t lcid, uint16_t* mtu, uint16_t* rcid,
-                              uint16_t* handle);
-
-/*******************************************************************************
- *
  *  Function        L2CA_CancelBleConnectReq
  *
  *  Description     Cancel a pending connection attempt to a BLE device.
diff --git a/main/shim/l2cap.cc b/main/shim/l2cap.cc
index 98db9cb..405cf76 100644
--- a/main/shim/l2cap.cc
+++ b/main/shim/l2cap.cc
@@ -17,69 +17,85 @@
 
 #include <cstdint>
 
+#include "main/shim/dumpsys.h"
 #include "main/shim/entry.h"
 #include "main/shim/l2cap.h"
 #include "main/shim/shim.h"
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
 
-constexpr size_t kBtHdrSize = sizeof(BT_HDR);
-constexpr uint16_t kInvalidConnectionInterfaceDescriptor = 0;
-constexpr bool kDisconnectResponseRequired = false;
-constexpr uint16_t kConnectionSuccess = 0;
+#include "shim/l2cap.h"
 
-bool bluetooth::legacy::shim::PsmData::IsPsmAllocated(uint16_t psm) const {
+namespace {
+constexpr char kModuleName[] = "shim::legacy::L2cap";
+constexpr bool kDisconnectResponseRequired = false;
+constexpr size_t kBtHdrSize = sizeof(BT_HDR);
+constexpr uint16_t kConnectionFail = 1;
+constexpr uint16_t kConnectionSuccess = 0;
+constexpr uint16_t kInvalidConnectionInterfaceDescriptor = 0;
+constexpr uint8_t kUnusedId = 0;
+constexpr uint16_t kUnusedResult = 0;
+}  // namespace
+
+bool bluetooth::shim::legacy::PsmManager::IsPsmRegistered(uint16_t psm) const {
   return psm_to_callback_map_.find(psm) != psm_to_callback_map_.end();
 }
 
-bool bluetooth::legacy::shim::PsmData::IsPsmRegistered(uint16_t psm) const {
-  return IsPsmAllocated(psm) && psm_to_callback_map_.at(psm) != nullptr;
+bool bluetooth::shim::legacy::PsmManager::HasClient(uint16_t psm) const {
+  return IsPsmRegistered(psm) && psm_to_callback_map_.at(psm) != nullptr;
 }
 
-void bluetooth::legacy::shim::PsmData::AllocatePsm(uint16_t psm) {
-  RegisterPsm(psm, nullptr);
-}
-
-void bluetooth::legacy::shim::PsmData::RegisterPsm(
+void bluetooth::shim::legacy::PsmManager::RegisterPsm(
     uint16_t psm, const tL2CAP_APPL_INFO* callbacks) {
+  CHECK(!HasClient(psm));
   psm_to_callback_map_[psm] = callbacks;
 }
 
-void bluetooth::legacy::shim::PsmData::UnregisterPsm(uint16_t psm) {
-  psm_to_callback_map_[psm] = nullptr;
+void bluetooth::shim::legacy::PsmManager::RegisterPsm(uint16_t psm) {
+  RegisterPsm(psm, nullptr);
 }
 
-void bluetooth::legacy::shim::PsmData::DeallocatePsm(uint16_t psm) {
+void bluetooth::shim::legacy::PsmManager::UnregisterPsm(uint16_t psm) {
+  CHECK(IsPsmRegistered(psm));
   psm_to_callback_map_.erase(psm);
 }
 
-const tL2CAP_APPL_INFO* bluetooth::legacy::shim::PsmData::Callbacks(
+const tL2CAP_APPL_INFO* bluetooth::shim::legacy::PsmManager::Callbacks(
     uint16_t psm) {
-  if (psm_to_callback_map_.find(psm) == psm_to_callback_map_.end()) {
-    LOG_WARN(LOG_TAG, "Accessing unknown psm:%hd:", psm);
-    return nullptr;
-  }
+  CHECK(HasClient(psm));
   return psm_to_callback_map_[psm];
 }
 
-bluetooth::legacy::shim::L2cap::L2cap()
+bluetooth::shim::legacy::L2cap::L2cap()
     : classic_dynamic_psm_(kInitialClassicDynamicPsm),
       le_dynamic_psm_(kInitialLeDynamicPsm),
-      classic_virtual_psm_(kInitialClassicVirtualPsm) {}
+      classic_virtual_psm_(kInitialClassicVirtualPsm) {
+  bluetooth::shim::RegisterDumpsysFunction(static_cast<void*>(this),
+                                           [this](int fd) { Dump(fd); });
+}
 
-bluetooth::legacy::shim::PsmData& bluetooth::legacy::shim::L2cap::Le() {
+bluetooth::shim::legacy::L2cap::~L2cap() {
+  bluetooth::shim::UnregisterDumpsysFunction(static_cast<void*>(this));
+}
+
+bluetooth::shim::legacy::PsmManager& bluetooth::shim::legacy::L2cap::Le() {
   return le_;
 }
 
-bluetooth::legacy::shim::PsmData& bluetooth::legacy::shim::L2cap::Classic() {
+bluetooth::shim::legacy::PsmManager& bluetooth::shim::legacy::L2cap::Classic() {
   return classic_;
 }
 
-bool bluetooth::legacy::shim::L2cap::ConnectionExists(uint16_t cid) const {
+bool bluetooth::shim::legacy::L2cap::ConnectionExists(uint16_t cid) const {
   return cid_to_psm_map_.find(cid) != cid_to_psm_map_.end();
 }
 
-uint16_t bluetooth::legacy::shim::L2cap::ConvertClientToRealPsm(
+uint16_t bluetooth::shim::legacy::L2cap::CidToPsm(uint16_t cid) const {
+  CHECK(ConnectionExists(cid));
+  return cid_to_psm_map_.at(cid);
+}
+
+uint16_t bluetooth::shim::legacy::L2cap::ConvertClientToRealPsm(
     uint16_t client_psm, bool is_outgoing_only_connection) {
   if (!is_outgoing_only_connection) {
     return client_psm;
@@ -87,7 +103,7 @@
   return GetNextVirtualPsm(client_psm);
 }
 
-uint16_t bluetooth::legacy::shim::L2cap::ConvertClientToRealPsm(
+uint16_t bluetooth::shim::legacy::L2cap::ConvertClientToRealPsm(
     uint16_t client_psm) {
   if (client_psm_to_real_psm_map_.find(client_psm) ==
       client_psm_to_real_psm_map_.end()) {
@@ -96,19 +112,19 @@
   return client_psm_to_real_psm_map_.at(client_psm);
 }
 
-void bluetooth::legacy::shim::L2cap::RemoveClientPsm(uint16_t client_psm) {
+void bluetooth::shim::legacy::L2cap::RemoveClientPsm(uint16_t client_psm) {
   if (client_psm_to_real_psm_map_.find(client_psm) !=
       client_psm_to_real_psm_map_.end()) {
     client_psm_to_real_psm_map_.erase(client_psm);
   }
 }
 
-uint16_t bluetooth::legacy::shim::L2cap::GetNextVirtualPsm(uint16_t real_psm) {
+uint16_t bluetooth::shim::legacy::L2cap::GetNextVirtualPsm(uint16_t real_psm) {
   if (real_psm < kInitialClassicDynamicPsm) {
     return real_psm;
   }
 
-  while (Classic().IsPsmAllocated(classic_virtual_psm_)) {
+  while (Classic().IsPsmRegistered(classic_virtual_psm_)) {
     classic_virtual_psm_ += 2;
     if (classic_virtual_psm_ >= kFinalClassicVirtualPsm) {
       classic_virtual_psm_ = kInitialClassicVirtualPsm;
@@ -117,8 +133,8 @@
   return classic_virtual_psm_;
 }
 
-uint16_t bluetooth::legacy::shim::L2cap::GetNextDynamicLePsm() {
-  while (Le().IsPsmAllocated(le_dynamic_psm_)) {
+uint16_t bluetooth::shim::legacy::L2cap::GetNextDynamicLePsm() {
+  while (Le().IsPsmRegistered(le_dynamic_psm_)) {
     le_dynamic_psm_++;
     if (le_dynamic_psm_ > kFinalLeDynamicPsm) {
       le_dynamic_psm_ = kInitialLeDynamicPsm;
@@ -127,8 +143,8 @@
   return le_dynamic_psm_;
 }
 
-uint16_t bluetooth::legacy::shim::L2cap::GetNextDynamicClassicPsm() {
-  while (Classic().IsPsmAllocated(classic_dynamic_psm_)) {
+uint16_t bluetooth::shim::legacy::L2cap::GetNextDynamicClassicPsm() {
+  while (Classic().IsPsmRegistered(classic_dynamic_psm_)) {
     classic_dynamic_psm_ += 2;
     if (classic_dynamic_psm_ > kFinalClassicDynamicPsm) {
       classic_dynamic_psm_ = kInitialClassicDynamicPsm;
@@ -146,201 +162,204 @@
   return classic_dynamic_psm_;
 }
 
-void bluetooth::legacy::shim::L2cap::RegisterService(
-    uint16_t psm, const tL2CAP_APPL_INFO* callbacks, bool enable_snoop) {
-  LOG_DEBUG(LOG_TAG, "Registering service on psm:%hd", psm);
-
+uint16_t bluetooth::shim::legacy::L2cap::RegisterService(
+    uint16_t psm, const tL2CAP_APPL_INFO* callbacks, bool enable_snoop,
+    tL2CAP_ERTM_INFO* p_ertm_info) {
+  if (Classic().IsPsmRegistered(psm)) {
+    LOG_WARN("Service is already registered psm:%hd", psm);
+    return 0;
+  }
   if (!enable_snoop) {
-    LOG_WARN(LOG_TAG, "UNIMPLEMENTED Cannot disable snooping on psm:%d", psm);
+    LOG_INFO("Disable snooping on psm basis unsupported psm:%d", psm);
   }
 
-  Classic().RegisterPsm(psm, callbacks);
-
-  std::promise<void> register_completed;
-  auto completed = register_completed.get_future();
+  LOG_DEBUG("Registering service on psm:%hd", psm);
+  RegisterServicePromise register_promise;
+  auto service_registered = register_promise.get_future();
+  bool use_ertm = false;
+  if (p_ertm_info != nullptr &&
+      p_ertm_info->preferred_mode == L2CAP_FCR_ERTM_MODE) {
+    use_ertm = true;
+  }
+  constexpr auto mtu = 1000;  // TODO: Let client decide
   bluetooth::shim::GetL2cap()->RegisterService(
-      psm,
+      psm, use_ertm, mtu,
       std::bind(
-          &bluetooth::legacy::shim::L2cap::OnRemoteInitiatedConnectionCreated,
+          &bluetooth::shim::legacy::L2cap::OnRemoteInitiatedConnectionCreated,
           this, std::placeholders::_1, std::placeholders::_2,
-          std::placeholders::_3),
-      std::move(register_completed));
-  completed.wait();
-  LOG_DEBUG(LOG_TAG, "Successfully registered service on psm:%hd", psm);
+          std::placeholders::_3, std::placeholders::_4),
+      std::move(register_promise));
+
+  uint16_t registered_psm = service_registered.get();
+  if (registered_psm != psm) {
+    LOG_WARN("Unable to register psm:%hd", psm);
+  } else {
+    LOG_DEBUG("Successfully registered psm:%hd", psm);
+    Classic().RegisterPsm(registered_psm, callbacks);
+  }
+  return registered_psm;
 }
 
-void bluetooth::legacy::shim::L2cap::OnRemoteInitiatedConnectionCreated(
-    std::string string_address, uint16_t psm, uint16_t cid) {
-  RawAddress raw_address;
-  RawAddress::FromString(string_address, raw_address);
-
-  LOG_DEBUG(LOG_TAG,
-            "Sending connection indicator to upper stack from device:%s "
-            "psm:%hd cid:%hd",
-            string_address.c_str(), psm, cid);
-
-  CHECK(!ConnectionExists(cid));
-  cid_to_psm_map_[cid] = psm;
-  SetCallbacks(cid, Classic().Callbacks(psm));
-  const tL2CAP_APPL_INFO* callbacks = Classic().Callbacks(psm);
-  CHECK(callbacks != nullptr);
-  callbacks->pL2CA_ConnectInd_Cb(raw_address, cid, psm /* UNUSED */,
-                                 0 /* UNUSED */);
-}
-
-void bluetooth::legacy::shim::L2cap::UnregisterService(uint16_t psm) {
+void bluetooth::shim::legacy::L2cap::UnregisterService(uint16_t psm) {
   if (!Classic().IsPsmRegistered(psm)) {
-    LOG_WARN(LOG_TAG,
-             "Service must be registered in order to unregister psm:%hd", psm);
+    LOG_WARN("Service must be registered in order to unregister psm:%hd", psm);
     return;
   }
-  LOG_DEBUG(LOG_TAG, "Unregistering service on psm:%hd", psm);
-  // TODO(cmanton) Check for open channels before unregistering
-  bluetooth::shim::GetL2cap()->UnregisterService(psm);
+  for (auto& entry : cid_to_psm_map_) {
+    if (entry.second == psm) {
+      LOG_WARN("  Unregistering service with active channels psm:%hd cid:%hd",
+               psm, entry.first);
+    }
+  }
+
+  LOG_DEBUG("Unregistering service on psm:%hd", psm);
+  UnregisterServicePromise unregister_promise;
+  auto service_unregistered = unregister_promise.get_future();
+  bluetooth::shim::GetL2cap()->UnregisterService(psm,
+                                                 std::move(unregister_promise));
+  service_unregistered.wait();
   Classic().UnregisterPsm(psm);
-  Classic().DeallocatePsm(psm);
 }
 
-uint16_t bluetooth::legacy::shim::L2cap::CreateConnection(
+uint16_t bluetooth::shim::legacy::L2cap::CreateConnection(
     uint16_t psm, const RawAddress& raw_address) {
   if (!Classic().IsPsmRegistered(psm)) {
-    LOG_WARN(LOG_TAG, "Service must be registered in order to connect psm:%hd",
-             psm);
+    LOG_WARN("Service must be registered in order to connect psm:%hd", psm);
     return kInvalidConnectionInterfaceDescriptor;
   }
 
-  std::promise<uint16_t> connect_completed;
-  auto completed = connect_completed.get_future();
-  LOG_DEBUG(LOG_TAG,
-            "Starting local initiated connection to psm:%hd address:%s", psm,
+  CreateConnectionPromise create_promise;
+  auto created = create_promise.get_future();
+  LOG_DEBUG("Initiating local connection to psm:%hd address:%s", psm,
             raw_address.ToString().c_str());
 
   bluetooth::shim::GetL2cap()->CreateConnection(
       psm, raw_address.ToString(),
       std::bind(
-          &bluetooth::legacy::shim::L2cap::OnLocalInitiatedConnectionCreated,
+          &bluetooth::shim::legacy::L2cap::OnLocalInitiatedConnectionCreated,
           this, std::placeholders::_1, std::placeholders::_2,
-          std::placeholders::_3),
-      std::move(connect_completed));
+          std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
+      std::move(create_promise));
 
-  uint16_t cid = completed.get();
+  uint16_t cid = created.get();
   if (cid == kInvalidConnectionInterfaceDescriptor) {
-    LOG_WARN(LOG_TAG,
-             "Failed to allocate resources to connect to psm:%hd address:%s",
+    LOG_WARN("Failed to initiate connection interface to psm:%hd address:%s",
              psm, raw_address.ToString().c_str());
   } else {
-    LOG_DEBUG(LOG_TAG,
-              "Successfully started connection to psm:%hd address:%s"
-              " connection_interface_descriptor:%hd",
-              psm, raw_address.ToString().c_str(), cid);
+    LOG_DEBUG(
+        "Successfully initiated connection to psm:%hd address:%s"
+        " connection_interface_descriptor:%hd",
+        psm, raw_address.ToString().c_str(), cid);
     CHECK(!ConnectionExists(cid));
     cid_to_psm_map_[cid] = psm;
-    SetCallbacks(cid, Classic().Callbacks(psm));
   }
   return cid;
 }
 
-void bluetooth::legacy::shim::L2cap::OnLocalInitiatedConnectionCreated(
-    std::string string_address, uint16_t psm, uint16_t cid) {
-  LOG_DEBUG(LOG_TAG,
-            "Sending connection confirm to the upper stack but really "
-            "a connection to %s has already been done cid:%hd",
-            string_address.c_str(), cid);
-  // TODO(cmanton) Make sure the device is correct for locally initiated
-  const tL2CAP_APPL_INFO* callbacks = Classic().Callbacks(psm);
-  CHECK(callbacks != nullptr);
-  callbacks->pL2CA_ConnectCfm_Cb(cid, kConnectionSuccess);
-};
+void bluetooth::shim::legacy::L2cap::OnLocalInitiatedConnectionCreated(
+    std::string string_address, uint16_t psm, uint16_t cid, uint16_t remote_cid,
+    bool connected) {
+  cid_to_remote_cid_map_[cid] = remote_cid;
+  if (cid_closing_set_.count(cid) == 0) {
+    if (connected) {
+      SetDownstreamCallbacks(cid);
+    } else {
+      LOG_WARN("Failed intitiating connection remote:%s psm:%hd cid:%hd",
+               string_address.c_str(), psm, cid);
+    }
+    Classic().Callbacks(psm)->pL2CA_ConnectCfm_Cb(
+        cid, connected ? (kConnectionSuccess) : (kConnectionFail));
+  } else {
+    LOG_DEBUG("Connection Closed before presentation to upper layer");
+    if (connected) {
+      SetDownstreamCallbacks(cid);
+      bluetooth::shim::GetL2cap()->CloseConnection(cid);
+    } else {
+      LOG_DEBUG("Connection failed after initiator closed");
+    }
+  }
+}
 
-bool bluetooth::legacy::shim::L2cap::Write(uint16_t cid, BT_HDR* bt_hdr) {
+void bluetooth::shim::legacy::L2cap::OnRemoteInitiatedConnectionCreated(
+    std::string string_address, uint16_t psm, uint16_t cid,
+    uint16_t remote_cid) {
+  RawAddress raw_address;
+  RawAddress::FromString(string_address, raw_address);
+
+  LOG_DEBUG(
+      "Sending connection indicator to upper stack from device:%s "
+      "psm:%hd cid:%hd",
+      string_address.c_str(), psm, cid);
+
+  CHECK(!ConnectionExists(cid));
+  cid_to_psm_map_[cid] = psm;
+  cid_to_remote_cid_map_[cid] = remote_cid;
+  SetDownstreamCallbacks(cid);
+  Classic().Callbacks(psm)->pL2CA_ConnectInd_Cb(raw_address, cid, psm,
+                                                kUnusedId);
+}
+
+bool bluetooth::shim::legacy::L2cap::Write(uint16_t cid, BT_HDR* bt_hdr) {
   CHECK(bt_hdr != nullptr);
   const uint8_t* data = bt_hdr->data + bt_hdr->offset;
   size_t len = bt_hdr->len;
   if (!ConnectionExists(cid) || len == 0) {
     return false;
   }
-  LOG_DEBUG(LOG_TAG, "Writing data cid:%hd len:%zd", cid, len);
+  LOG_DEBUG("Writing data cid:%hd len:%zd", cid, len);
   bluetooth::shim::GetL2cap()->Write(cid, data, len);
   return true;
 }
 
-bool bluetooth::legacy::shim::L2cap::WriteFlushable(uint16_t cid,
-                                                    BT_HDR* bt_hdr) {
-  CHECK(bt_hdr != nullptr);
-  const uint8_t* data = bt_hdr->data + bt_hdr->offset;
-  size_t len = bt_hdr->len;
-  if (!ConnectionExists(cid) || len == 0) {
-    return false;
-  }
-  bluetooth::shim::GetL2cap()->WriteFlushable(cid, data, len);
-  return true;
-}
-
-bool bluetooth::legacy::shim::L2cap::WriteNonFlushable(uint16_t cid,
-                                                       BT_HDR* bt_hdr) {
-  CHECK(bt_hdr != nullptr);
-  const uint8_t* data = bt_hdr->data + bt_hdr->offset;
-  size_t len = bt_hdr->len;
-  if (!ConnectionExists(cid) || len == 0) {
-    return false;
-  }
-  bluetooth::shim::GetL2cap()->WriteNonFlushable(cid, data, len);
-  return true;
-}
-
-bool bluetooth::legacy::shim::L2cap::SetCallbacks(
-    uint16_t cid, const tL2CAP_APPL_INFO* callbacks) {
-  CHECK(callbacks != nullptr);
-  CHECK(ConnectionExists(cid));
-  LOG_ASSERT(cid_to_callback_map_.find(cid) == cid_to_callback_map_.end())
-      << "Already have callbacks registered for "
-         "connection_interface_descriptor:"
-      << cid;
-  cid_to_callback_map_[cid] = callbacks;
-
+void bluetooth::shim::legacy::L2cap::SetDownstreamCallbacks(uint16_t cid) {
   bluetooth::shim::GetL2cap()->SetReadDataReadyCallback(
       cid, [this](uint16_t cid, std::vector<const uint8_t> data) {
-        LOG_DEBUG(LOG_TAG, "OnDataReady cid:%hd len:%zd", cid, data.size());
+        LOG_DEBUG("OnDataReady cid:%hd len:%zd", cid, data.size());
         BT_HDR* bt_hdr =
             static_cast<BT_HDR*>(osi_calloc(data.size() + kBtHdrSize));
         std::copy(data.begin(), data.end(), bt_hdr->data);
         bt_hdr->len = data.size();
-        CHECK(cid_to_callback_map_.find(cid) != cid_to_callback_map_.end());
-        cid_to_callback_map_[cid]->pL2CA_DataInd_Cb(cid, bt_hdr);
+        classic_.Callbacks(CidToPsm(cid))->pL2CA_DataInd_Cb(cid, bt_hdr);
       });
 
   bluetooth::shim::GetL2cap()->SetConnectionClosedCallback(
       cid, [this](uint16_t cid, int error_code) {
-        LOG_DEBUG(LOG_TAG, "OnChannel closed callback cid:%hd", cid);
-        CHECK(cid_to_callback_map_.find(cid) != cid_to_callback_map_.end());
-        cid_to_callback_map_[cid]->pL2CA_DisconnectInd_Cb(
-            cid, kDisconnectResponseRequired);
-        cid_to_callback_map_.erase(cid);
+        LOG_DEBUG("OnChannel closed callback cid:%hd", cid);
+        if (!ConnectionExists(cid)) {
+          LOG_WARN("%s Unexpected channel closure cid:%hd", __func__, cid);
+          return;
+        }
+        if (cid_closing_set_.count(cid) == 1) {
+          cid_closing_set_.erase(cid);
+          classic_.Callbacks(CidToPsm(cid))
+              ->pL2CA_DisconnectCfm_Cb(cid, kUnusedResult);
+        } else {
+          classic_.Callbacks(CidToPsm(cid))
+              ->pL2CA_DisconnectInd_Cb(cid, kDisconnectResponseRequired);
+        }
         cid_to_psm_map_.erase(cid);
+        cid_to_remote_cid_map_.erase(cid);
       });
-  return true;
 }
 
-bool bluetooth::legacy::shim::L2cap::ConnectResponse(
+bool bluetooth::shim::legacy::L2cap::ConnectResponse(
     const RawAddress& raw_address, uint8_t signal_id, uint16_t cid,
     uint16_t result, uint16_t status, tL2CAP_ERTM_INFO* ertm_info) {
   CHECK(ConnectionExists(cid));
-  LOG_DEBUG(LOG_TAG,
-            "%s Silently dropping client connect response as channel is "
-            "already connected",
-            __func__);
+  LOG_DEBUG(
+      "%s Silently dropping client connect response as channel is "
+      "already connected",
+      __func__);
   return true;
 }
 
-bool bluetooth::legacy::shim::L2cap::ConfigRequest(
+bool bluetooth::shim::legacy::L2cap::ConfigRequest(
     uint16_t cid, const tL2CAP_CFG_INFO* config_info) {
+  LOG_INFO("Received config request from upper layer cid:%hd", cid);
   CHECK(ConnectionExists(cid));
-  LOG_INFO(LOG_TAG, "Received config request from upper layer cid:%hd", cid);
 
   bluetooth::shim::GetL2cap()->SendLoopbackResponse([this, cid]() {
     CHECK(ConnectionExists(cid));
-    const tL2CAP_APPL_INFO* callbacks = cid_to_callback_map_[cid];
-    CHECK(callbacks != nullptr);
     tL2CAP_CFG_INFO cfg_info{
         .result = L2CAP_CFG_OK,
         .mtu_present = false,
@@ -351,33 +370,60 @@
         .ext_flow_spec_present = false,
         .flags = 0,
     };
-    callbacks->pL2CA_ConfigCfm_Cb(cid, &cfg_info);
-    callbacks->pL2CA_ConfigInd_Cb(cid, &cfg_info);
+    classic_.Callbacks(CidToPsm(cid))->pL2CA_ConfigCfm_Cb(cid, &cfg_info);
+    classic_.Callbacks(CidToPsm(cid))->pL2CA_ConfigInd_Cb(cid, &cfg_info);
   });
   return true;
 }
 
-bool bluetooth::legacy::shim::L2cap::ConfigResponse(
+bool bluetooth::shim::legacy::L2cap::ConfigResponse(
     uint16_t cid, const tL2CAP_CFG_INFO* config_info) {
   CHECK(ConnectionExists(cid));
   LOG_DEBUG(
-      LOG_TAG,
+
       "%s Silently dropping client config response as channel is already open",
       __func__);
   return true;
 }
 
-bool bluetooth::legacy::shim::L2cap::DisconnectRequest(uint16_t cid) {
+bool bluetooth::shim::legacy::L2cap::DisconnectRequest(uint16_t cid) {
   CHECK(ConnectionExists(cid));
-  cid_to_callback_map_.erase(cid);
+  if (cid_closing_set_.find(cid) != cid_closing_set_.end()) {
+    LOG_WARN("%s Channel already in closing state cid:%hu", __func__, cid);
+    return false;
+  }
+  LOG_DEBUG("%s initiated locally cid:%hu", __func__, cid);
+  cid_closing_set_.insert(cid);
   bluetooth::shim::GetL2cap()->CloseConnection(cid);
   return true;
 }
 
-bool bluetooth::legacy::shim::L2cap::DisconnectResponse(uint16_t cid) {
-  LOG_DEBUG(LOG_TAG,
-            "%s Silently dropping client disconnect response as channel is "
-            "already disconnected",
-            __func__);
+bool bluetooth::shim::legacy::L2cap::DisconnectResponse(uint16_t cid) {
+  LOG_DEBUG(
+      "%s Silently dropping client disconnect response as channel is "
+      "already disconnected",
+      __func__);
+  return true;
+}
+
+void bluetooth::shim::legacy::L2cap::Dump(int fd) {
+  if (cid_to_psm_map_.empty()) {
+    dprintf(fd, "%s No active l2cap channels\n", kModuleName);
+  } else {
+    for (auto& connection : cid_to_psm_map_) {
+      dprintf(fd, "%s active l2cap channel cid:%hd psm:%hd\n", kModuleName,
+              connection.first, connection.second);
+    }
+  }
+}
+
+bool bluetooth::shim::legacy::L2cap::GetRemoteCid(uint16_t cid,
+                                                  uint16_t* remote_cid) {
+  auto it = cid_to_remote_cid_map_.find(cid);
+  if (it == cid_to_remote_cid_map_.end()) {
+    return false;
+  }
+
+  *remote_cid = it->second;
   return true;
 }
diff --git a/main/shim/l2cap.h b/main/shim/l2cap.h
index 2fbe0d6..0db97c6 100644
--- a/main/shim/l2cap.h
+++ b/main/shim/l2cap.h
@@ -17,13 +17,15 @@
 #pragma once
 
 #include <cstdint>
+#include <set>
 #include <unordered_map>
 
+#include "main/shim/dumpsys.h"
 #include "stack/include/l2c_api.h"
 
 namespace bluetooth {
-namespace legacy {
 namespace shim {
+namespace legacy {
 
 static constexpr uint16_t kInitialClassicDynamicPsm = 0x1001;
 static constexpr uint16_t kFinalClassicDynamicPsm = 0xfeff;
@@ -32,38 +34,45 @@
 static constexpr uint16_t kInitialLeDynamicPsm = 0x0080;
 static constexpr uint16_t kFinalLeDynamicPsm = 0x00ff;
 
-using PsmData = struct {
-  bool IsPsmAllocated(uint16_t psm) const;
+class PsmManager {
+ public:
   bool IsPsmRegistered(uint16_t psm) const;
-
-  void AllocatePsm(uint16_t psm);
+  bool HasClient(uint16_t psm) const;
   void RegisterPsm(uint16_t psm, const tL2CAP_APPL_INFO* callbacks);
-
+  void RegisterPsm(uint16_t psm);
   void UnregisterPsm(uint16_t psm);
-  void DeallocatePsm(uint16_t psm);
-
   const tL2CAP_APPL_INFO* Callbacks(uint16_t psm);
 
  private:
+  /**
+   * Mapping of psm to client callback.
+   *
+   * The current API allows a client may reserve a psm but not
+   * provide a callback which is reflected in a mapping of a
+   * valid psm key entry but a nullptr value.
+   *
+   * A valid client is indicated with a valid psm key entry and a
+   * non-nullptr value.
+   */
   std::unordered_map<uint16_t, const tL2CAP_APPL_INFO*> psm_to_callback_map_;
 };
 
 class L2cap {
  public:
-  void RegisterService(uint16_t psm, const tL2CAP_APPL_INFO* callbacks,
-                       bool enable_snoop);
+  uint16_t RegisterService(uint16_t psm, const tL2CAP_APPL_INFO* callbacks,
+                           bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info);
   void UnregisterService(uint16_t psm);
 
   uint16_t CreateConnection(uint16_t psm, const RawAddress& raw_address);
 
   bool Write(uint16_t cid, BT_HDR* bt_hdr);
-  bool WriteFlushable(uint16_t cid, BT_HDR* bt_hdr);
-  bool WriteNonFlushable(uint16_t cid, BT_HDR* bt_hdr);
 
   void OnLocalInitiatedConnectionCreated(std::string string_address,
-                                         uint16_t psm, uint16_t cid);
+                                         uint16_t psm, uint16_t cid,
+                                         uint16_t remote_cid, bool connected);
   void OnRemoteInitiatedConnectionCreated(std::string string_addresss,
-                                          uint16_t psm, uint16_t cid);
+                                          uint16_t psm, uint16_t cid,
+                                          uint16_t remote_cid);
 
   uint16_t GetNextDynamicClassicPsm();
   uint16_t GetNextDynamicLePsm();
@@ -82,19 +91,25 @@
   bool DisconnectRequest(uint16_t cid);
   bool DisconnectResponse(uint16_t cid);
 
-  L2cap();
+  bool GetRemoteCid(uint16_t cid, uint16_t* remote_cid);
 
-  PsmData& Classic();
-  PsmData& Le();
+  L2cap();
+  ~L2cap();
+
+  PsmManager& Classic();
+  PsmManager& Le();
 
  private:
   uint16_t GetNextVirtualPsm(uint16_t real_psm);
-  bool SetCallbacks(uint16_t cid, const tL2CAP_APPL_INFO* callbacks);
+  void SetDownstreamCallbacks(uint16_t cid);
 
-  PsmData classic_;
-  PsmData le_;
+  void Dump(int fd);
+
+  PsmManager classic_;
+  PsmManager le_;
 
   bool ConnectionExists(uint16_t cid) const;
+  uint16_t CidToPsm(uint16_t cid) const;
 
   uint16_t classic_dynamic_psm_;
   uint16_t le_dynamic_psm_;
@@ -103,11 +118,13 @@
   std::unordered_map<uint16_t,
                      std::function<void(std::function<void(uint16_t c)>)>>
       cid_to_postable_map_;
+  std::set<uint16_t> cid_closing_set_;
+
   std::unordered_map<uint16_t, uint16_t> cid_to_psm_map_;
+  std::unordered_map<uint16_t, uint16_t> cid_to_remote_cid_map_;
   std::unordered_map<uint16_t, uint16_t> client_psm_to_real_psm_map_;
-  std::unordered_map<uint16_t, const tL2CAP_APPL_INFO*> cid_to_callback_map_;
 };
 
-}  // namespace shim
 }  // namespace legacy
+}  // namespace shim
 }  // namespace bluetooth
diff --git a/main/shim/l2cap_test.cc b/main/shim/l2cap_test.cc
deleted file mode 100644
index bf28f3c..0000000
--- a/main/shim/l2cap_test.cc
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <cstdint>
-
-#define LOG_TAG "bt_shim_test"
-
-#include "osi/include/log.h"
-#include "shim/l2cap.h"
-#include "shim/test_stack.h"
-#include "types/raw_address.h"
-
-TestStack test_stack_;
-
-bluetooth::shim::IStack* bluetooth::shim::GetGabeldorscheStack() {
-  return (bluetooth::shim::IStack*)&test_stack_;
-}
-
-namespace bluetooth {
-namespace legacy {
-
-namespace {
-
-constexpr uint16_t kPsm = 123;
-constexpr uint16_t kCid = 987;
-constexpr size_t kDataBufferSize = 1024;
-
-uint8_t bt_hdr_data[] = {
-    0x00, 0x00,                                     /* event */
-    0x08, 0x00,                                     /* len */
-    0x00, 0x00,                                     /* offset */
-    0x00, 0x00,                                     /* layer specific */
-    0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, /* data */
-    0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, /* data */
-};
-
-class L2capTest;
-L2capTest* l2cap_test_ = nullptr;
-
-class L2capTest : public ::testing::Test {
- public:
-  static shim::L2cap* l2cap_;
-
-  struct {
-    int L2caConnectCfmCb;
-    int L2caConnectPndCb;
-    int L2caConfigIndCb;
-    int L2caConfigCfmCb;
-    int L2caDisconnectIndCb;
-    int L2caDisconnectCfmCb;
-    int L2caQosViolationIndCb;
-    int L2caDataIndCb;
-    int L2caCongestionStatusCb;
-    int L2caTxCompleteCb;
-    int L2caCreditsReceivedCb;
-  } cnt_{
-      .L2caConnectCfmCb = 0,
-      .L2caConnectPndCb = 0,
-      .L2caConfigIndCb = 0,
-      .L2caConfigCfmCb = 0,
-      .L2caDisconnectIndCb = 0,
-      .L2caDisconnectCfmCb = 0,
-      .L2caQosViolationIndCb = 0,
-      .L2caDataIndCb = 0,
-      .L2caCongestionStatusCb = 0,
-      .L2caTxCompleteCb = 0,
-      .L2caCreditsReceivedCb = 0,
-  };
-
- protected:
-  void SetUp() override {
-    l2cap_ = new shim::L2cap();
-    l2cap_test_ = this;
-  }
-
-  void TearDown() override {
-    delete l2cap_;
-    l2cap_ = nullptr;
-  }
-
-  uint8_t data_buffer_[kDataBufferSize];
-};
-
-shim::L2cap* L2capTest::l2cap_ = nullptr;
-// Indication of remotely initiated connection response sent
-void L2caConnectIndCb(const RawAddress& raw_address, uint16_t a, uint16_t b,
-                      uint8_t c) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-// Confirms locally initiated connection request completed
-void L2caConnectCfmCb(uint16_t cid, uint16_t result) {
-  l2cap_test_->cnt_.L2caConnectCfmCb++;
-  LOG_INFO(LOG_TAG, "%s cid:%hd result:%hd", __func__, cid, result);
-}
-
-void L2caConnectPndCb(uint16_t cid) {
-  l2cap_test_->cnt_.L2caConnectPndCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-// Indication of remotely initiated configuration response sent
-void L2caConfigIndCb(uint16_t cid, tL2CAP_CFG_INFO* callbacks) {
-  l2cap_test_->cnt_.L2caConfigIndCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-// Confirms locally initiated config request completed
-void L2caConfigCfmCb(uint16_t cid, tL2CAP_CFG_INFO* callbacks) {
-  l2cap_test_->cnt_.L2caConfigCfmCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-// Indication of remotely initiated disconnection response sent
-void L2caDisconnectIndCb(uint16_t cid, bool needs_ack) {
-  l2cap_test_->cnt_.L2caDisconnectIndCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-// Confirms locally initiated disconnect request completed
-void L2caDisconnectCfmCb(uint16_t cid, uint16_t result) {
-  l2cap_test_->cnt_.L2caDisconnectCfmCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-void L2caQosViolationIndCb(const RawAddress& raw_address) {
-  l2cap_test_->cnt_.L2caQosViolationIndCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-void L2caDataIndCb(uint16_t cid, BT_HDR* bt_hdr) {
-  l2cap_test_->cnt_.L2caDataIndCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-void L2caCongestionStatusCb(uint16_t cid, bool is_congested) {
-  l2cap_test_->cnt_.L2caCongestionStatusCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-void L2caTxCompleteCb(uint16_t cid, uint16_t sdu_cnt) {
-  l2cap_test_->cnt_.L2caTxCompleteCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-void L2caCreditsReceivedCb(uint16_t cid, uint16_t credits_received,
-                           uint16_t credit_count) {
-  l2cap_test_->cnt_.L2caCreditsReceivedCb++;
-  LOG_INFO(LOG_TAG, "%s", __func__);
-}
-
-tL2CAP_APPL_INFO test_callbacks{
-    .pL2CA_ConnectInd_Cb = L2caConnectIndCb,
-    .pL2CA_ConnectCfm_Cb = L2caConnectCfmCb,
-    .pL2CA_ConnectPnd_Cb = L2caConnectPndCb,
-    .pL2CA_ConfigInd_Cb = L2caConfigIndCb,
-    .pL2CA_ConfigCfm_Cb = L2caConfigCfmCb,
-    .pL2CA_DisconnectInd_Cb = L2caDisconnectIndCb,
-    .pL2CA_DisconnectCfm_Cb = L2caDisconnectCfmCb,
-    .pL2CA_QoSViolationInd_Cb = L2caQosViolationIndCb,
-    .pL2CA_DataInd_Cb = L2caDataIndCb,
-    .pL2CA_CongestionStatus_Cb = L2caCongestionStatusCb,
-    .pL2CA_TxComplete_Cb = L2caTxCompleteCb,
-    .pL2CA_CreditsReceived_Cb = L2caCreditsReceivedCb,
-};
-
-TEST_F(L2capTest, RegisterService) {
-  l2cap_->RegisterService(kPsm, &test_callbacks, false);
-  CHECK(test_stack_.test_l2cap_.registered_service_.count(kPsm) == 1);
-}
-
-TEST_F(L2capTest, UnregisterService) {
-  l2cap_->RegisterService(kPsm, &test_callbacks, false);
-  CHECK(test_stack_.test_l2cap_.registered_service_.count(kPsm) == 1);
-  l2cap_->UnregisterService(kPsm);
-  CHECK(test_stack_.test_l2cap_.registered_service_.count(kPsm) == 0);
-}
-
-TEST_F(L2capTest, CreateConnection_NotRegistered) {
-  RawAddress raw_address;
-  std::string string_address("11:22:33:44:55:66");
-  RawAddress::FromString(string_address, raw_address);
-  uint16_t cid = l2cap_->CreateConnection(kPsm, raw_address);
-  CHECK(cid == 0);
-}
-
-TEST_F(L2capTest, CreateConnection_Registered) {
-  test_stack_.test_l2cap_.cid_ = kCid;
-  l2cap_->RegisterService(kPsm, &test_callbacks, false);
-
-  RawAddress raw_address;
-  std::string string_address("11:22:33:44:55:66");
-  RawAddress::FromString(string_address, raw_address);
-  uint16_t cid = l2cap_->CreateConnection(kPsm, raw_address);
-  CHECK(cid != 0);
-}
-
-TEST_F(L2capTest, CreateConnection_ConnectResponse) {
-  test_stack_.test_l2cap_.cid_ = kCid;
-  l2cap_->RegisterService(kPsm, &test_callbacks, false);
-
-  RawAddress raw_address;
-  std::string string_address("11:22:33:44:55:66");
-  RawAddress::FromString(string_address, raw_address);
-  uint16_t cid = l2cap_->CreateConnection(kPsm, raw_address);
-  CHECK(cid != 0);
-
-  CHECK(l2cap_->ConnectResponse(raw_address, 0, cid, 0, 0, nullptr));
-}
-
-TEST_F(L2capTest, CreateConnection_ConfigRequest) {
-  test_stack_.test_l2cap_.cid_ = kCid;
-  l2cap_->RegisterService(kPsm, &test_callbacks, false);
-
-  RawAddress raw_address;
-  std::string string_address("11:22:33:44:55:66");
-  RawAddress::FromString(string_address, raw_address);
-  uint16_t cid = l2cap_->CreateConnection(kPsm, raw_address);
-  CHECK(cid != 0);
-
-  // Simulate a successful connection response
-  l2cap_->OnLocalInitiatedConnectionCreated("11:22:33:44:55:66", kPsm, kCid);
-  CHECK(cnt_.L2caConnectCfmCb == 1);
-
-  CHECK(l2cap_->ConfigRequest(cid, nullptr));
-}
-
-TEST_F(L2capTest, CreateConnection_ConfigResponse) {
-  test_stack_.test_l2cap_.cid_ = kCid;
-  l2cap_->RegisterService(kPsm, &test_callbacks, false);
-
-  RawAddress raw_address;
-  std::string string_address("11:22:33:44:55:66");
-  RawAddress::FromString(string_address, raw_address);
-  uint16_t cid = l2cap_->CreateConnection(kPsm, raw_address);
-  CHECK(cid != 0);
-
-  // Simulate a successful connection response
-  l2cap_->OnLocalInitiatedConnectionCreated("11:22:33:44:55:66", kPsm, kCid);
-  CHECK(cnt_.L2caConnectCfmCb == 1);
-
-  CHECK(l2cap_->ConfigResponse(cid, nullptr));
-}
-
-TEST_F(L2capTest, CreateConnection_DisconnectRequest) {
-  test_stack_.test_l2cap_.cid_ = kCid;
-  l2cap_->RegisterService(kPsm, &test_callbacks, false);
-
-  RawAddress raw_address;
-  std::string string_address("11:22:33:44:55:66");
-  RawAddress::FromString(string_address, raw_address);
-  uint16_t cid = l2cap_->CreateConnection(kPsm, raw_address);
-  CHECK(cid != 0);
-
-  // Simulate a successful connection response
-  l2cap_->OnLocalInitiatedConnectionCreated("11:22:33:44:55:66", kPsm, kCid);
-  CHECK(cnt_.L2caConnectCfmCb == 1);
-
-  CHECK(l2cap_->DisconnectRequest(cid));
-}
-
-TEST_F(L2capTest, CreateConnection_DisconnectResponse) {
-  test_stack_.test_l2cap_.cid_ = kCid;
-  l2cap_->RegisterService(kPsm, &test_callbacks, false);
-
-  RawAddress raw_address;
-  std::string string_address("11:22:33:44:55:66");
-  RawAddress::FromString(string_address, raw_address);
-  uint16_t cid = l2cap_->CreateConnection(kPsm, raw_address);
-  CHECK(cid != 0);
-
-  // Simulate a successful connection response
-  l2cap_->OnLocalInitiatedConnectionCreated("11:22:33:44:55:66", kPsm, kCid);
-  CHECK(cnt_.L2caConnectCfmCb == 1);
-
-  CHECK(l2cap_->DisconnectResponse(cid));
-}
-
-TEST_F(L2capTest, CreateConnection_WithHandshake) {
-  test_stack_.test_l2cap_.cid_ = kCid;
-  l2cap_->RegisterService(kPsm, &test_callbacks, false);
-
-  RawAddress raw_address;
-  std::string string_address("11:22:33:44:55:66");
-  RawAddress::FromString(string_address, raw_address);
-  uint16_t cid = l2cap_->CreateConnection(kPsm, raw_address);
-  CHECK(cid != 0);
-
-  // Simulate a successful connection response
-  l2cap_->OnLocalInitiatedConnectionCreated("11:22:33:44:55:66", kPsm, kCid);
-  CHECK(cnt_.L2caConnectCfmCb == 1);
-
-  CHECK(l2cap_->ConfigRequest(cid, nullptr) == true);
-  CHECK(cnt_.L2caConfigCfmCb == 1);
-  CHECK(cnt_.L2caConfigIndCb == 1);
-
-  BT_HDR* bt_hdr = (BT_HDR*)bt_hdr_data;
-
-  test_stack_.test_l2cap_.data_buffer_ = data_buffer_;
-  test_stack_.test_l2cap_.data_buffer_size_ = kDataBufferSize;
-
-  l2cap_->Write(cid, bt_hdr);
-
-  CHECK(data_buffer_[0] == 0x11);
-  CHECK(data_buffer_[1] == 0x22);
-  CHECK(data_buffer_[2] == 0x33);
-  CHECK(data_buffer_[3] == 0x44);
-  CHECK(data_buffer_[4] == 0x55);
-  CHECK(data_buffer_[5] == 0x66);
-  CHECK(data_buffer_[6] == 0x77);
-  CHECK(data_buffer_[7] == 0x88);
-  CHECK(data_buffer_[8] == 0x00);
-}
-
-}  // namespace
-}  // namespace legacy
-}  // namespace bluetooth
diff --git a/main/shim/shim.cc b/main/shim/shim.cc
index 9af17d6..b6a5748 100644
--- a/main/shim/shim.cc
+++ b/main/shim/shim.cc
@@ -14,29 +14,50 @@
  * limitations under the License.
  */
 
-#include <cstdint>
+#define LOG_TAG "bt_shim"
 
-#include "main/shim/entry.h"
 #include "main/shim/shim.h"
-#include "osi/include/properties.h"
+#include "main/shim/entry.h"
+#include "main/shim/stack.h"
 
-static const char* kPropertyKey = "bluetooth.gd.enabled";
+#include "gd/common/init_flags.h"
+#include "gd/os/log.h"
 
-static bool gd_shim_enabled_ = false;
-static bool gd_shim_property_checked_ = false;
+future_t* IdleModuleStartUp() {
+  bluetooth::shim::Stack::GetInstance()->StartIdleMode();
+  return kReturnImmediate;
+}
+
+future_t* ShimModuleStartUp() {
+  bluetooth::shim::Stack::GetInstance()->StartEverything();
+  return kReturnImmediate;
+}
+
+future_t* GeneralShutDown() {
+  bluetooth::shim::Stack::GetInstance()->Stop();
+  return kReturnImmediate;
+}
+
+EXPORT_SYMBOL extern const module_t gd_idle_module = {
+    .name = GD_IDLE_MODULE,
+    .init = kUnusedModuleApi,
+    .start_up = IdleModuleStartUp,
+    .shut_down = GeneralShutDown,
+    .clean_up = kUnusedModuleApi,
+    .dependencies = {kUnusedModuleDependencies}};
 
 EXPORT_SYMBOL extern const module_t gd_shim_module = {
     .name = GD_SHIM_MODULE,
-    .init = nullptr,
-    .start_up = bluetooth::shim::StartGabeldorscheStack,
-    .shut_down = bluetooth::shim::StopGabeldorscheStack,
-    .clean_up = NULL,
-    .dependencies = {NULL}};
+    .init = kUnusedModuleApi,
+    .start_up = ShimModuleStartUp,
+    .shut_down = GeneralShutDown,
+    .clean_up = kUnusedModuleApi,
+    .dependencies = {kUnusedModuleDependencies}};
 
 bool bluetooth::shim::is_gd_shim_enabled() {
-  if (!gd_shim_property_checked_) {
-    gd_shim_property_checked_ = true;
-    gd_shim_enabled_ = (osi_property_get_int32(kPropertyKey, 0) == 1);
-  }
-  return gd_shim_enabled_;
+  return common::InitFlags::GdCoreEnabled();
+}
+
+bool bluetooth::shim::is_gd_stack_started_up() {
+  return bluetooth::shim::Stack::GetInstance()->IsRunning();
 }
diff --git a/main/shim/shim.h b/main/shim/shim.h
index 38fb208..29a80f7 100644
--- a/main/shim/shim.h
+++ b/main/shim/shim.h
@@ -21,13 +21,38 @@
  */
 #include "btcore/include/module.h"
 #include "main/shim/entry.h"
+#include "osi/include/future.h"
 
+static const char GD_IDLE_MODULE[] = "gd_idle_module";
 static const char GD_SHIM_MODULE[] = "gd_shim_module";
 
+constexpr future_t* kReturnImmediate = nullptr;
+constexpr module_lifecycle_fn kUnusedModuleApi = nullptr;
+constexpr char* kUnusedModuleDependencies = nullptr;
+
 namespace bluetooth {
 namespace shim {
 
+/**
+ * Checks if the bluetooth stack is running in legacy or gd mode.
+ *
+ * This check is used throughout the legacy stack to determine which
+ * methods, classes or functions to invoke.  The default (false) mode
+ * is the legacy mode which runs the original legacy bluetooth stack.
+ * When enabled (true) the core portion of the gd stack is invoked
+ * at key points to execute equivalent functionality using the
+ * gd core components.
+ *
+ * @return true if using gd shim core, false if using legacy.
+ */
 bool is_gd_shim_enabled();
 
+/**
+ * Checks if the bluetooth gd stack has been started up.
+ *
+ * @return true if bluetooth gd stack is started, false otherwise.
+ */
+bool is_gd_stack_started_up();
+
 }  // namespace shim
 }  // namespace bluetooth
diff --git a/main/shim/stack.cc b/main/shim/stack.cc
new file mode 100644
index 0000000..e8a665b
--- /dev/null
+++ b/main/shim/stack.cc
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_gd_shim"
+
+#include "gd/att/att_module.h"
+#include "gd/hal/hci_hal.h"
+#include "gd/hci/acl_manager.h"
+#include "gd/hci/hci_layer.h"
+#include "gd/hci/le_advertising_manager.h"
+#include "gd/hci/le_scanning_manager.h"
+#include "gd/l2cap/classic/l2cap_classic_module.h"
+#include "gd/l2cap/le/l2cap_le_module.h"
+#include "gd/neighbor/connectability.h"
+#include "gd/neighbor/discoverability.h"
+#include "gd/neighbor/inquiry.h"
+#include "gd/neighbor/name.h"
+#include "gd/neighbor/name_db.h"
+#include "gd/neighbor/page.h"
+#include "gd/neighbor/scan.h"
+#include "gd/os/log.h"
+#include "gd/security/security_module.h"
+#include "gd/shim/dumpsys.h"
+#include "gd/shim/l2cap.h"
+#include "gd/storage/storage_module.h"
+
+#include "main/shim/stack.h"
+
+namespace bluetooth {
+namespace shim {
+
+Stack* Stack::GetInstance() {
+  static Stack instance;
+  return &instance;
+}
+
+void Stack::StartIdleMode() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT_LOG(!is_running_, "%s Gd stack already running", __func__);
+  LOG_INFO("%s Starting Gd stack", __func__);
+  ModuleList modules;
+  modules.add<storage::StorageModule>();
+  Start(&modules);
+  // Make sure the leaf modules are started
+  ASSERT(stack_manager_.GetInstance<storage::StorageModule>() != nullptr);
+  is_running_ = true;
+}
+
+void Stack::StartEverything() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT_LOG(!is_running_, "%s Gd stack already running", __func__);
+  LOG_INFO("%s Starting Gd stack", __func__);
+  ModuleList modules;
+  modules.add<att::AttModule>();
+  modules.add<hal::HciHal>();
+  modules.add<hci::AclManager>();
+  modules.add<hci::HciLayer>();
+  modules.add<hci::LeAdvertisingManager>();
+  modules.add<hci::LeScanningManager>();
+  modules.add<l2cap::classic::L2capClassicModule>();
+  modules.add<l2cap::le::L2capLeModule>();
+  modules.add<neighbor::ConnectabilityModule>();
+  modules.add<neighbor::DiscoverabilityModule>();
+  modules.add<neighbor::InquiryModule>();
+  modules.add<neighbor::NameModule>();
+  modules.add<neighbor::NameDbModule>();
+  modules.add<neighbor::PageModule>();
+  modules.add<neighbor::ScanModule>();
+  modules.add<security::SecurityModule>();
+  modules.add<storage::StorageModule>();
+  modules.add<shim::Dumpsys>();
+  modules.add<shim::L2cap>();
+  Start(&modules);
+  // Make sure the leaf modules are started
+  ASSERT(stack_manager_.GetInstance<storage::StorageModule>() != nullptr);
+  ASSERT(stack_manager_.GetInstance<shim::L2cap>() != nullptr);
+  ASSERT(stack_manager_.GetInstance<shim::Dumpsys>() != nullptr);
+  btm_ = new Btm(stack_handler_,
+                 stack_manager_.GetInstance<neighbor::InquiryModule>());
+  is_running_ = true;
+}
+
+void Stack::Start(ModuleList* modules) {
+  ASSERT_LOG(!is_running_, "%s Gd stack already running", __func__);
+  LOG_DEBUG("%s Starting Gd stack", __func__);
+
+  stack_thread_ =
+      new os::Thread("gd_stack_thread", os::Thread::Priority::NORMAL);
+  stack_manager_.StartUp(modules, stack_thread_);
+
+  stack_handler_ = new os::Handler(stack_thread_);
+
+  LOG_INFO("%s Successfully toggled Gd stack", __func__);
+}
+
+void Stack::Stop() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT_LOG(is_running_, "%s Gd stack not running", __func__);
+  is_running_ = false;
+
+  delete btm_;
+  btm_ = nullptr;
+
+  stack_handler_->Clear();
+  delete stack_handler_;
+  stack_handler_ = nullptr;
+
+  stack_manager_.ShutDown();
+  stack_thread_->Stop();
+  delete stack_thread_;
+  stack_thread_ = nullptr;
+
+  LOG_INFO("%s Successfully shut down Gd stack", __func__);
+}
+
+bool Stack::IsRunning() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  return is_running_;
+}
+
+StackManager* Stack::GetStackManager() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(is_running_);
+  return &stack_manager_;
+}
+
+Btm* Stack::GetBtm() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(is_running_);
+  return btm_;
+}
+
+os::Handler* Stack::GetHandler() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(is_running_);
+  return stack_handler_;
+}
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/stack.h b/main/shim/stack.h
new file mode 100644
index 0000000..9b761a3
--- /dev/null
+++ b/main/shim/stack.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+
+#include "main/shim/btm.h"
+
+#include "gd/module.h"
+#include "gd/os/handler.h"
+#include "gd/os/thread.h"
+#include "gd/os/utils.h"
+#include "gd/stack_manager.h"
+
+// The shim layer implementation on the Gd stack side.
+namespace bluetooth {
+namespace shim {
+
+// GD shim stack, having modes corresponding to legacy stack
+class Stack {
+ public:
+  static Stack* GetInstance();
+
+  Stack() = default;
+  ~Stack() = default;
+
+  // Idle mode, config is loaded, but controller is not enabled
+  void StartIdleMode();
+  // Running mode, everything is up
+  void StartEverything();
+
+  void Stop();
+  bool IsRunning();
+
+  StackManager* GetStackManager();
+  Btm* GetBtm();
+  os::Handler* GetHandler();
+
+  DISALLOW_COPY_AND_ASSIGN(Stack);
+
+ private:
+  std::mutex mutex_;
+  StackManager stack_manager_;
+  bool is_running_ = false;
+  os::Thread* stack_thread_ = nullptr;
+  os::Handler* stack_handler_ = nullptr;
+  Btm* btm_ = nullptr;
+
+  void Start(ModuleList* modules);
+};
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/test_stack.cc b/main/shim/test_stack.cc
deleted file mode 100644
index 9d38541..0000000
--- a/main/shim/test_stack.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <cstdint>
-#include <future>
-
-#include "gd/shim/only_include_this_file_into_legacy_stack___ever.h"
-#include "main/shim/entry.h"
-#include "main/shim/test_stack.h"
-#include "osi/include/log.h"
-
-#define ASSERT(condition)                                    \
-  do {                                                       \
-    if (!(condition)) {                                      \
-      LOG_ALWAYS_FATAL("assertion '" #condition "' failed"); \
-    }                                                        \
-  } while (false)
-
-void TestGdShimL2cap::RegisterService(
-    uint16_t psm, bluetooth::shim::ConnectionOpenCallback on_open,
-    std::promise<void> completed) {
-  completed.set_value();
-  registered_service_.insert(psm);
-}
-
-void TestGdShimL2cap::UnregisterService(uint16_t psm) {
-  registered_service_.erase(psm);
-}
-
-void TestGdShimL2cap::CreateConnection(
-    uint16_t psm, const std::string address,
-    bluetooth::shim::ConnectionOpenCallback on_open,
-    std::promise<uint16_t> completed) {
-  completed.set_value(cid_);
-}
-
-void TestGdShimL2cap::CloseConnection(uint16_t cid) {}
-
-void TestGdShimL2cap::SetReadDataReadyCallback(
-    uint16_t cid, bluetooth::shim::ReadDataReadyCallback on_data_ready) {}
-
-void TestGdShimL2cap::SetConnectionClosedCallback(
-    uint16_t cid, bluetooth::shim::ConnectionClosedCallback on_closed) {}
-
-void TestGdShimL2cap::Write(uint16_t cid, const uint8_t* data, size_t len) {
-  ASSERT(data_buffer_ != nullptr);
-  ASSERT(data_buffer_size_ > len);
-  memcpy(data_buffer_, data, len);
-}
-
-void TestGdShimL2cap::WriteFlushable(uint16_t cid, const uint8_t* data,
-                                     size_t len) {}
-
-void TestGdShimL2cap::WriteNonFlushable(uint16_t cid, const uint8_t* data,
-                                        size_t len) {}
-
-void TestGdShimL2cap::SendLoopbackResponse(std::function<void()> function) {
-  function();
-}
-
-void TestStack::Start() {}
-
-void TestStack::Stop() {}
-
-bluetooth::shim::IAdvertising* TestStack::GetAdvertising() { return nullptr; }
-
-bluetooth::shim::IController* TestStack::GetController() { return nullptr; }
-
-bluetooth::shim::IConnectability* TestStack::GetConnectability() {
-  return nullptr;
-}
-
-bluetooth::shim::IDiscoverability* TestStack::GetDiscoverability() {
-  return nullptr;
-}
-
-bluetooth::shim::IHciLayer* TestStack::GetHciLayer() { return nullptr; }
-
-bluetooth::shim::IInquiry* TestStack::GetInquiry() { return nullptr; }
-
-bluetooth::shim::IL2cap* TestStack::GetL2cap() { return &test_l2cap_; }
-
-bluetooth::shim::IName* TestStack::GetName() { return nullptr; }
-
-bluetooth::shim::IPage* TestStack::GetPage() { return nullptr; }
-
-bluetooth::shim::IScanning* TestStack::GetScanning() { return nullptr; }
diff --git a/main/shim/test_stack.h b/main/shim/test_stack.h
deleted file mode 100644
index 92027b1..0000000
--- a/main/shim/test_stack.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <cstdint>
-#include <future>
-#include <set>
-
-#include "gd/shim/only_include_this_file_into_legacy_stack___ever.h"
-#include "main/shim/entry.h"
-
-class TestGdShimL2cap : public bluetooth::shim::IL2cap {
- public:
-  uint16_t cid_{0};
-  bool write_success_{false};
-  bool is_congested_{false};
-  uint8_t* data_buffer_{nullptr};
-  size_t data_buffer_size_{0};
-  std::set<uint16_t /* psm */> registered_service_;
-
-  void RegisterService(uint16_t psm,
-                       bluetooth::shim::ConnectionOpenCallback on_open,
-                       std::promise<void> completed) override;
-  void UnregisterService(uint16_t psm);
-  void CreateConnection(uint16_t psm, const std::string address,
-                        bluetooth::shim::ConnectionOpenCallback on_open,
-                        std::promise<uint16_t> completed) override;
-  void CloseConnection(uint16_t cid);
-  void SetReadDataReadyCallback(
-      uint16_t cid,
-      bluetooth::shim::ReadDataReadyCallback on_data_ready) override;
-  void SetConnectionClosedCallback(
-      uint16_t cid,
-      bluetooth::shim::ConnectionClosedCallback on_closed) override;
-  void Write(uint16_t cid, const uint8_t* data, size_t len) override;
-  void WriteFlushable(uint16_t cid, const uint8_t* data, size_t len) override;
-  void WriteNonFlushable(uint16_t cid, const uint8_t* data,
-                         size_t len) override;
-  void SendLoopbackResponse(std::function<void()>) override;
-};
-
-class TestStack : public bluetooth::shim::IStack {
- public:
-  TestStack() = default;
-
-  bluetooth::shim::IAdvertising* GetAdvertising();
-  bluetooth::shim::IController* GetController();
-  bluetooth::shim::IConnectability* GetConnectability();
-  bluetooth::shim::IDiscoverability* GetDiscoverability();
-  bluetooth::shim::IHciLayer* GetHciLayer();
-  bluetooth::shim::IInquiry* GetInquiry();
-  bluetooth::shim::IL2cap* GetL2cap();
-  bluetooth::shim::IName* GetName();
-  bluetooth::shim::IPage* GetPage();
-  bluetooth::shim::IScanning* GetScanning();
-
-  TestGdShimL2cap test_l2cap_;
-
-  void Start();
-  void Stop();
-};
diff --git a/main/stack_config.cc b/main/stack_config.cc
index 0e56250..c6dbe4d 100644
--- a/main/stack_config.cc
+++ b/main/stack_config.cc
@@ -48,11 +48,11 @@
 #endif  // defined(OS_GENERIC)
   CHECK(path != NULL);
 
-  LOG_INFO(LOG_TAG, "%s attempt to load stack conf from %s", __func__, path);
+  LOG_INFO("%s attempt to load stack conf from %s", __func__, path);
 
   config = config_new(path);
   if (!config) {
-    LOG_INFO(LOG_TAG, "%s file >%s< not found", __func__, path);
+    LOG_INFO("%s file >%s< not found", __func__, path);
     config = config_new_empty();
   }
 
diff --git a/osi/Android.bp b/osi/Android.bp
index bcdaf5f..3f652f4 100644
--- a/osi/Android.bp
+++ b/osi/Android.bp
@@ -122,6 +122,7 @@
         "libbt-protos-lite",
         "libgmock",
         "libosi",
+        "libc++fs",
     ],
     target: {
         linux_glibc: {
diff --git a/osi/include/config.h b/osi/include/config.h
index 47c3a3e..a74314d 100644
--- a/osi/include/config.h
+++ b/osi/include/config.h
@@ -33,10 +33,15 @@
 struct section_t {
   std::string name;
   std::list<entry_t> entries;
+  void Set(std::string key, std::string value);
+  std::list<entry_t>::iterator Find(const std::string& key);
+  bool Has(const std::string& key);
 };
 
 struct config_t {
   std::list<section_t> sections;
+  std::list<section_t>::iterator Find(const std::string& section);
+  bool Has(const std::string& section);
 };
 
 // Creates a new config object with no entries (i.e. not backed by a file).
diff --git a/osi/include/log.h b/osi/include/log.h
index 1c2e96d..0961201 100644
--- a/osi/include/log.h
+++ b/osi/include/log.h
@@ -18,69 +18,4 @@
 
 #pragma once
 
-/*
- * TODO(armansito): Work-around until we figure out a way to generate logs in a
- * platform-independent manner.
- */
-#if defined(OS_GENERIC)
-
-/* syslog didn't work well here since we would be redefining LOG_DEBUG. */
-#include <stdio.h>
-
-#define LOGWRAPPER(tag, fmt, args...) \
-  fprintf(stderr, "%s: " fmt "\n", tag, ##args)
-
-#define LOG_VERBOSE(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_DEBUG(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_INFO(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_WARN(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_ERROR(...) LOGWRAPPER(__VA_ARGS__)
-
-#define LOG_EVENT_INT(...)
-
-#else /* !defined(OS_GENERIC) */
-
-#include <log/log.h>
-
-/**
- * These log statements are effectively executing only ALOG(_________, tag, fmt,
- * ## args ).
- * fprintf is only to cause compilation error when LOG_TAG is not provided,
- * which breaks build on Linux (for OS_GENERIC).
- */
-
-#if LOG_NDEBUG
-#define LOG_VERBOSE(tag, fmt, args...)                          \
-  do {                                                          \
-    (true) ? ((int)0) : fprintf(stderr, "%s" fmt, tag, ##args); \
-  } while (0)
-#else  // LOG_NDEBUG
-#define LOG_VERBOSE(tag, fmt, args...)               \
-  do {                                               \
-    (true) ? ALOG(LOG_VERBOSE, tag, fmt, ##args)     \
-           : fprintf(stderr, "%s" fmt, tag, ##args); \
-  } while (0)
-#endif  // !LOG_NDEBUG
-
-#define LOG_DEBUG(tag, fmt, args...)                 \
-  do {                                               \
-    (true) ? ALOG(LOG_DEBUG, tag, fmt, ##args)       \
-           : fprintf(stderr, "%s" fmt, tag, ##args); \
-  } while (0)
-#define LOG_INFO(tag, fmt, args...)                  \
-  do {                                               \
-    (true) ? ALOG(LOG_INFO, tag, fmt, ##args)        \
-           : fprintf(stderr, "%s" fmt, tag, ##args); \
-  } while (0)
-#define LOG_WARN(tag, fmt, args...)                  \
-  do {                                               \
-    (true) ? ALOG(LOG_WARN, tag, fmt, ##args)        \
-           : fprintf(stderr, "%s" fmt, tag, ##args); \
-  } while (0)
-#define LOG_ERROR(tag, fmt, args...)                 \
-  do {                                               \
-    (true) ? ALOG(LOG_ERROR, tag, fmt, ##args)       \
-           : fprintf(stderr, "%s" fmt, tag, ##args); \
-  } while (0)
-
-#endif /* defined(OS_GENERIC) */
+#include "gd/os/log.h"
diff --git a/osi/src/alarm.cc b/osi/src/alarm.cc
index bc1eee5..1170880 100644
--- a/osi/src/alarm.cc
+++ b/osi/src/alarm.cc
@@ -317,7 +317,7 @@
 
   alarms = list_new(NULL);
   if (!alarms) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate alarm list.", __func__);
+    LOG_ERROR("%s unable to allocate alarm list.", __func__);
     goto error;
   }
 
@@ -333,22 +333,20 @@
 
   alarm_expired = semaphore_new(0);
   if (!alarm_expired) {
-    LOG_ERROR(LOG_TAG, "%s unable to create alarm expired semaphore", __func__);
+    LOG_ERROR("%s unable to create alarm expired semaphore", __func__);
     goto error;
   }
 
   default_callback_thread =
       thread_new_sized("alarm_default_callbacks", SIZE_MAX);
   if (default_callback_thread == NULL) {
-    LOG_ERROR(LOG_TAG, "%s unable to create default alarm callbacks thread.",
-              __func__);
+    LOG_ERROR("%s unable to create default alarm callbacks thread.", __func__);
     goto error;
   }
   thread_set_rt_priority(default_callback_thread, THREAD_RT_PRIORITY);
   default_callback_queue = fixed_queue_new(SIZE_MAX);
   if (default_callback_queue == NULL) {
-    LOG_ERROR(LOG_TAG, "%s unable to create default alarm callbacks queue.",
-              __func__);
+    LOG_ERROR("%s unable to create default alarm callbacks queue.", __func__);
     goto error;
   }
   alarm_register_processing_queue(default_callback_queue,
@@ -357,7 +355,7 @@
   dispatcher_thread_active = true;
   dispatcher_thread = thread_new("alarm_dispatcher");
   if (!dispatcher_thread) {
-    LOG_ERROR(LOG_TAG, "%s unable to create alarm callback thread.", __func__);
+    LOG_ERROR("%s unable to create alarm callback thread.", __func__);
     goto error;
   }
   thread_set_rt_priority(dispatcher_thread, THREAD_RT_PRIORITY);
@@ -393,8 +391,7 @@
 
   struct timespec ts;
   if (clock_gettime(CLOCK_ID, &ts) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to get current time: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to get current time: %s", __func__, strerror(errno));
     return 0;
   }
 
@@ -476,7 +473,7 @@
   if (next_expiration < TIMER_INTERVAL_FOR_WAKELOCK_IN_MS) {
     if (!timer_set) {
       if (!wakelock_acquire()) {
-        LOG_ERROR(LOG_TAG, "%s unable to acquire wake lock", __func__);
+        LOG_ERROR("%s unable to acquire wake lock", __func__);
         goto done;
       }
     }
@@ -510,8 +507,7 @@
     wakeup_time.it_value.tv_sec = (next->deadline_ms / 1000);
     wakeup_time.it_value.tv_nsec = (next->deadline_ms % 1000) * 1000000LL;
     if (timer_settime(wakeup_timer, TIMER_ABSTIME, &wakeup_time, NULL) == -1)
-      LOG_ERROR(LOG_TAG, "%s unable to set wakeup timer: %s", __func__,
-                strerror(errno));
+      LOG_ERROR("%s unable to set wakeup timer: %s", __func__, strerror(errno));
   }
 
 done:
@@ -522,7 +518,7 @@
   }
 
   if (timer_settime(timer, TIMER_ABSTIME, &timer_time, NULL) == -1)
-    LOG_ERROR(LOG_TAG, "%s unable to set timer: %s", __func__, strerror(errno));
+    LOG_ERROR("%s unable to set timer: %s", __func__, strerror(errno));
 
   // If next expiration was in the past (e.g. short timer that got context
   // switched) then the timer might have diarmed itself. Detect this case and
@@ -540,7 +536,7 @@
     if (time_to_expire.it_value.tv_sec == 0 &&
         time_to_expire.it_value.tv_nsec == 0) {
       LOG_DEBUG(
-          LOG_TAG,
+
           "%s alarm expiration too close for posix timers, switching to guns",
           __func__);
       semaphore_post(alarm_expired);
@@ -652,7 +648,7 @@
     // Enqueue the alarm for processing
     if (alarm->for_msg_loop) {
       if (!get_main_message_loop()) {
-        LOG_ERROR(LOG_TAG, "%s: message loop already NULL. Alarm: %s", __func__,
+        LOG_ERROR("%s: message loop already NULL. Alarm: %s", __func__,
                   alarm->stats.name);
         continue;
       }
@@ -665,7 +661,7 @@
     }
   }
 
-  LOG_DEBUG(LOG_TAG, "%s Callback thread exited", __func__);
+  LOG_DEBUG("%s Callback thread exited", __func__);
 }
 
 static bool timer_create_internal(const clockid_t clock_id, timer_t* timer) {
@@ -685,17 +681,17 @@
   sigevent.sigev_notify_function = (void (*)(union sigval))timer_callback;
   sigevent.sigev_notify_attributes = &thread_attr;
   if (timer_create(clock_id, &sigevent, timer) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to create timer with clock %d: %s", __func__,
-              clock_id, strerror(errno));
+    LOG_ERROR("%s unable to create timer with clock %d: %s", __func__, clock_id,
+              strerror(errno));
     if (clock_id == CLOCK_BOOTTIME_ALARM) {
-      LOG_ERROR(LOG_TAG,
-                "The kernel might not have support for "
-                "timer_create(CLOCK_BOOTTIME_ALARM): "
-                "https://lwn.net/Articles/429925/");
-      LOG_ERROR(LOG_TAG,
-                "See following patches: "
-                "https://git.kernel.org/cgit/linux/kernel/git/torvalds/"
-                "linux.git/log/?qt=grep&q=CLOCK_BOOTTIME_ALARM");
+      LOG_ERROR(
+          "The kernel might not have support for "
+          "timer_create(CLOCK_BOOTTIME_ALARM): "
+          "https://lwn.net/Articles/429925/");
+      LOG_ERROR(
+          "See following patches: "
+          "https://git.kernel.org/cgit/linux/kernel/git/torvalds/"
+          "linux.git/log/?qt=grep&q=CLOCK_BOOTTIME_ALARM");
     }
     return false;
   }
diff --git a/osi/src/allocation_tracker.cc b/osi/src/allocation_tracker.cc
index ba6b7f1..554bb4b 100644
--- a/osi/src/allocation_tracker.cc
+++ b/osi/src/allocation_tracker.cc
@@ -56,7 +56,7 @@
   // randomize the canary contents
   for (size_t i = 0; i < canary_size; i++) canary[i] = (char)osi_rand();
 
-  LOG_DEBUG(LOG_TAG, "canary initialized");
+  LOG_DEBUG("canary initialized");
 
   enabled = true;
 }
@@ -88,8 +88,7 @@
     if (!allocation->freed) {
       unfreed_memory_size +=
           allocation->size;  // Report back the unfreed byte count
-      LOG_ERROR(LOG_TAG,
-                "%s found unfreed allocation. address: 0x%zx size: %zd bytes",
+      LOG_ERROR("%s found unfreed allocation. address: 0x%zx size: %zd bytes",
                 __func__, (uintptr_t)allocation->ptr, allocation->size);
     }
   }
diff --git a/osi/src/array.cc b/osi/src/array.cc
index f28a70c..6ec419c 100644
--- a/osi/src/array.cc
+++ b/osi/src/array.cc
@@ -81,10 +81,10 @@
   CHECK(data != NULL);
 
   if (array->length == array->capacity && !grow(array)) {
-    LOG_ERROR(LOG_TAG,
-              "%s unable to grow array past current capacity of %zu elements "
-              "of size %zu.",
-              __func__, array->capacity, array->element_size);
+    LOG_ERROR(
+        "%s unable to grow array past current capacity of %zu elements "
+        "of size %zu.",
+        __func__, array->capacity, array->element_size);
     return false;
   }
 
diff --git a/osi/src/config.cc b/osi/src/config.cc
index de7e6e6..29d6206 100644
--- a/osi/src/config.cc
+++ b/osi/src/config.cc
@@ -30,11 +30,41 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
+
 #include <sstream>
 #include <type_traits>
 
-// Empty definition; this type is aliased to list_node_t.
-struct config_section_iter_t {};
+void section_t::Set(std::string key, std::string value) {
+  for (entry_t& entry : entries) {
+    if (entry.key == key) {
+      entry.value = value;
+      return;
+    }
+  }
+  // add a new key to the section
+  entries.emplace_back(
+      entry_t{.key = std::move(key), .value = std::move(value)});
+}
+
+std::list<entry_t>::iterator section_t::Find(const std::string& key) {
+  return std::find_if(
+      entries.begin(), entries.end(),
+      [&key](const entry_t& entry) { return entry.key == key; });
+}
+
+bool section_t::Has(const std::string& key) {
+  return Find(key) != entries.end();
+}
+
+std::list<section_t>::iterator config_t::Find(const std::string& section) {
+  return std::find_if(
+      sections.begin(), sections.end(),
+      [&section](const section_t& sec) { return sec.name == section; });
+}
+
+bool config_t::Has(const std::string& key) {
+  return Find(key) != sections.end();
+}
 
 static bool config_parse(FILE* fp, config_t* config);
 
diff --git a/osi/src/future.cc b/osi/src/future.cc
index e5347a4..203dbe5 100644
--- a/osi/src/future.cc
+++ b/osi/src/future.cc
@@ -40,8 +40,7 @@
 
   ret->semaphore = semaphore_new(0);
   if (!ret->semaphore) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate memory for the semaphore.",
-              __func__);
+    LOG_ERROR("%s unable to allocate memory for the semaphore.", __func__);
     goto error;
   }
 
diff --git a/osi/src/hash_map_utils.cc b/osi/src/hash_map_utils.cc
index 6cd58de..eb1e051 100644
--- a/osi/src/hash_map_utils.cc
+++ b/osi/src/hash_map_utils.cc
@@ -35,7 +35,7 @@
   char* str = osi_strdup(params);
   if (!str) return map;
 
-  LOG_VERBOSE(LOG_TAG, "%s: source string: '%s'", __func__, str);
+  LOG_VERBOSE("%s: source string: '%s'", __func__, str);
 
   // Parse |str| and add extracted key-and-value pair(s) in |map|.
   int items = 0;
@@ -69,7 +69,7 @@
     kvpair = strtok_r(NULL, ";", &tmpstr);
   }
 
-  if (!items) LOG_VERBOSE(LOG_TAG, "%s: no items found in string\n", __func__);
+  if (!items) LOG_VERBOSE("%s: no items found in string\n", __func__);
 
   osi_free(str);
   return map;
@@ -78,7 +78,6 @@
 void hash_map_utils_dump_string_keys_string_values(
     std::unordered_map<std::string, std::string>& map) {
   for (const auto& ptr : map) {
-    LOG_INFO(LOG_TAG, "key: '%s' value: '%s'\n", ptr.first.c_str(),
-             ptr.second.c_str());
+    LOG_INFO("key: '%s' value: '%s'\n", ptr.first.c_str(), ptr.second.c_str());
   }
 }
diff --git a/osi/src/osi.cc b/osi/src/osi.cc
index 73d6c1a..11c2c4b 100644
--- a/osi/src/osi.cc
+++ b/osi/src/osi.cc
@@ -37,7 +37,7 @@
   int rand_fd = open(RANDOM_PATH, O_RDONLY);
 
   if (rand_fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s can't open rand fd %s: %s ", __func__, RANDOM_PATH,
+    LOG_ERROR("%s can't open rand fd %s: %s ", __func__, RANDOM_PATH,
               strerror(errno));
     CHECK(rand_fd != INVALID_FD);
   }
diff --git a/osi/src/reactor.cc b/osi/src/reactor.cc
index 7898170..e7ed23f 100644
--- a/osi/src/reactor.cc
+++ b/osi/src/reactor.cc
@@ -74,23 +74,21 @@
 
   ret->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
   if (ret->epoll_fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s unable to create epoll instance: %s", __func__,
+    LOG_ERROR("%s unable to create epoll instance: %s", __func__,
               strerror(errno));
     goto error;
   }
 
   ret->event_fd = eventfd(0, 0);
   if (ret->event_fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s unable to create eventfd: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to create eventfd: %s", __func__, strerror(errno));
     goto error;
   }
 
   ret->list_mutex = new std::mutex;
   ret->invalidation_list = list_new(NULL);
   if (!ret->invalidation_list) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate object invalidation list.",
-              __func__);
+    LOG_ERROR("%s unable to allocate object invalidation list.", __func__);
     goto error;
   }
 
@@ -99,8 +97,8 @@
   event.events = EPOLLIN;
   event.data.ptr = NULL;
   if (epoll_ctl(ret->epoll_fd, EPOLL_CTL_ADD, ret->event_fd, &event) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to register eventfd with epoll set: %s",
-              __func__, strerror(errno));
+    LOG_ERROR("%s unable to register eventfd with epoll set: %s", __func__,
+              strerror(errno));
     goto error;
   }
 
@@ -159,8 +157,8 @@
   event.data.ptr = object;
 
   if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to register fd %d to epoll set: %s", __func__,
-              fd, strerror(errno));
+    LOG_ERROR("%s unable to register fd %d to epoll set: %s", __func__, fd,
+              strerror(errno));
     delete object->mutex;
     osi_free(object);
     return NULL;
@@ -182,8 +180,8 @@
 
   if (epoll_ctl(object->reactor->epoll_fd, EPOLL_CTL_MOD, object->fd, &event) ==
       -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to modify interest set for fd %d: %s",
-              __func__, object->fd, strerror(errno));
+    LOG_ERROR("%s unable to modify interest set for fd %d: %s", __func__,
+              object->fd, strerror(errno));
     return false;
   }
 
@@ -200,8 +198,8 @@
   reactor_t* reactor = obj->reactor;
 
   if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_DEL, obj->fd, NULL) == -1)
-    LOG_ERROR(LOG_TAG, "%s unable to unregister fd %d from epoll set: %s",
-              __func__, obj->fd, strerror(errno));
+    LOG_ERROR("%s unable to unregister fd %d from epoll set: %s", __func__,
+              obj->fd, strerror(errno));
 
   if (reactor->is_running &&
       pthread_equal(pthread_self(), reactor->run_thread)) {
@@ -247,8 +245,7 @@
     int ret;
     OSI_NO_INTR(ret = epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, -1));
     if (ret == -1) {
-      LOG_ERROR(LOG_TAG, "%s error in epoll_wait: %s", __func__,
-                strerror(errno));
+      LOG_ERROR("%s error in epoll_wait: %s", __func__, strerror(errno));
       reactor->is_running = false;
       return REACTOR_STATUS_ERROR;
     }
diff --git a/osi/src/semaphore.cc b/osi/src/semaphore.cc
index a943aa7..4175f9c 100644
--- a/osi/src/semaphore.cc
+++ b/osi/src/semaphore.cc
@@ -44,8 +44,7 @@
   semaphore_t* ret = static_cast<semaphore_t*>(osi_malloc(sizeof(semaphore_t)));
   ret->fd = eventfd(value, EFD_SEMAPHORE);
   if (ret->fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s unable to allocate semaphore: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to allocate semaphore: %s", __func__, strerror(errno));
     osi_free(ret);
     ret = NULL;
   }
@@ -65,8 +64,7 @@
 
   eventfd_t value;
   if (eventfd_read(semaphore->fd, &value) == -1)
-    LOG_ERROR(LOG_TAG, "%s unable to wait on semaphore: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to wait on semaphore: %s", __func__, strerror(errno));
 }
 
 bool semaphore_try_wait(semaphore_t* semaphore) {
@@ -75,13 +73,13 @@
 
   int flags = fcntl(semaphore->fd, F_GETFL);
   if (flags == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to get flags for semaphore fd: %s", __func__,
+    LOG_ERROR("%s unable to get flags for semaphore fd: %s", __func__,
               strerror(errno));
     return false;
   }
   if (fcntl(semaphore->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to set O_NONBLOCK for semaphore fd: %s",
-              __func__, strerror(errno));
+    LOG_ERROR("%s unable to set O_NONBLOCK for semaphore fd: %s", __func__,
+              strerror(errno));
     return false;
   }
 
@@ -90,8 +88,8 @@
   if (eventfd_read(semaphore->fd, &value) == -1) rc = false;
 
   if (fcntl(semaphore->fd, F_SETFL, flags) == -1)
-    LOG_ERROR(LOG_TAG, "%s unable to restore flags for semaphore fd: %s",
-              __func__, strerror(errno));
+    LOG_ERROR("%s unable to restore flags for semaphore fd: %s", __func__,
+              strerror(errno));
   return rc;
 }
 
@@ -100,8 +98,7 @@
   CHECK(semaphore->fd != INVALID_FD);
 
   if (eventfd_write(semaphore->fd, 1ULL) == -1)
-    LOG_ERROR(LOG_TAG, "%s unable to post to semaphore: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to post to semaphore: %s", __func__, strerror(errno));
 }
 
 int semaphore_get_fd(const semaphore_t* semaphore) {
diff --git a/osi/src/socket.cc b/osi/src/socket.cc
index 3f9366f..e26381a 100644
--- a/osi/src/socket.cc
+++ b/osi/src/socket.cc
@@ -54,15 +54,13 @@
 
   ret->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   if (ret->fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s unable to create socket: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to create socket: %s", __func__, strerror(errno));
     goto error;
   }
 
   if (setsockopt(ret->fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) ==
       -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to set SO_REUSEADDR: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to set SO_REUSEADDR: %s", __func__, strerror(errno));
     goto error;
   }
 
@@ -99,13 +97,13 @@
   addr.sin_addr.s_addr = htonl(LOCALHOST_);
   addr.sin_port = htons(port);
   if (bind(socket->fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to bind socket to port %u: %s", __func__,
-              port, strerror(errno));
+    LOG_ERROR("%s unable to bind socket to port %u: %s", __func__, port,
+              strerror(errno));
     return false;
   }
 
   if (listen(socket->fd, 10) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to listen on port %u: %s", __func__, port,
+    LOG_ERROR("%s unable to listen on port %u: %s", __func__, port,
               strerror(errno));
     return false;
   }
@@ -119,8 +117,7 @@
   int fd;
   OSI_NO_INTR(fd = accept(socket->fd, NULL, NULL));
   if (fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s unable to accept socket: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to accept socket: %s", __func__, strerror(errno));
     return NULL;
   }
 
diff --git a/osi/src/thread.cc b/osi/src/thread.cc
index e164083..e97ec33 100644
--- a/osi/src/thread.cc
+++ b/osi/src/thread.cc
@@ -150,8 +150,7 @@
 
   const int rc = setpriority(PRIO_PROCESS, thread->tid, priority);
   if (rc < 0) {
-    LOG_ERROR(LOG_TAG,
-              "%s unable to set thread priority %d for tid %d, error %d",
+    LOG_ERROR("%s unable to set thread priority %d for tid %d, error %d",
               __func__, priority, thread->tid, rc);
     return false;
   }
@@ -167,8 +166,7 @@
 
   const int rc = sched_setscheduler(thread->tid, SCHED_FIFO, &rt_params);
   if (rc != 0) {
-    LOG_ERROR(LOG_TAG,
-              "%s unable to set SCHED_FIFO priority %d for tid %d, error %s",
+    LOG_ERROR("%s unable to set SCHED_FIFO priority %d for tid %d, error %s",
               __func__, priority, thread->tid, strerror(errno));
     return false;
   }
@@ -200,16 +198,15 @@
   CHECK(thread != NULL);
 
   if (prctl(PR_SET_NAME, (unsigned long)thread->name) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to set thread name: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to set thread name: %s", __func__, strerror(errno));
     start->error = errno;
     semaphore_post(start->start_sem);
     return NULL;
   }
   thread->tid = gettid();
 
-  LOG_INFO(LOG_TAG, "%s: thread id %d, thread name %s started", __func__,
-           thread->tid, thread->name);
+  LOG_INFO("%s: thread id %d, thread name %s started", __func__, thread->tid,
+           thread->name);
 
   semaphore_post(start->start_sem);
 
@@ -236,10 +233,10 @@
   }
 
   if (count > fixed_queue_capacity(thread->work_queue))
-    LOG_DEBUG(LOG_TAG, "%s growing event queue on shutdown.", __func__);
+    LOG_DEBUG("%s growing event queue on shutdown.", __func__);
 
-  LOG_WARN(LOG_TAG, "%s: thread id %d, thread name %s exited", __func__,
-           thread->tid, thread->name);
+  LOG_WARN("%s: thread id %d, thread name %s exited", __func__, thread->tid,
+           thread->name);
   return NULL;
 }
 
diff --git a/osi/src/wakelock.cc b/osi/src/wakelock.cc
index 0f9a51b..7b41fbe 100644
--- a/osi/src/wakelock.cc
+++ b/osi/src/wakelock.cc
@@ -95,8 +95,7 @@
 void wakelock_set_os_callouts(bt_os_callouts_t* callouts) {
   wakelock_os_callouts = callouts;
   is_native = (wakelock_os_callouts == NULL);
-  LOG_INFO(LOG_TAG, "%s set to %s", __func__,
-           (is_native) ? "native" : "non-native");
+  LOG_INFO("%s set to %s", __func__, (is_native) ? "native" : "non-native");
 }
 
 bool wakelock_acquire(void) {
@@ -112,7 +111,7 @@
   update_wakelock_acquired_stats(status);
 
   if (status != BT_STATUS_SUCCESS)
-    LOG_ERROR(LOG_TAG, "%s unable to acquire wake lock: %d", __func__, status);
+    LOG_ERROR("%s unable to acquire wake lock: %d", __func__, status);
 
   return (status == BT_STATUS_SUCCESS);
 }
@@ -124,25 +123,23 @@
 
 static bt_status_t wakelock_acquire_native(void) {
   if (wake_lock_fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s lock not acquired, invalid fd", __func__);
+    LOG_ERROR("%s lock not acquired, invalid fd", __func__);
     return BT_STATUS_PARM_INVALID;
   }
 
   if (wake_unlock_fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s not acquiring lock: can't release lock", __func__);
+    LOG_ERROR("%s not acquiring lock: can't release lock", __func__);
     return BT_STATUS_PARM_INVALID;
   }
 
   long lock_name_len = strlen(WAKE_LOCK_ID);
   locked_id_len = write(wake_lock_fd, WAKE_LOCK_ID, lock_name_len);
   if (locked_id_len == -1) {
-    LOG_ERROR(LOG_TAG, "%s wake lock not acquired: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s wake lock not acquired: %s", __func__, strerror(errno));
     return BT_STATUS_FAIL;
   } else if (locked_id_len < lock_name_len) {
     // TODO (jamuraa): this is weird. maybe we should release and retry.
-    LOG_WARN(LOG_TAG, "%s wake lock truncated to %zd chars", __func__,
-             locked_id_len);
+    LOG_WARN("%s wake lock truncated to %zd chars", __func__, locked_id_len);
   }
   return BT_STATUS_SUCCESS;
 }
@@ -169,17 +166,16 @@
 
 static bt_status_t wakelock_release_native(void) {
   if (wake_unlock_fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s lock not released, invalid fd", __func__);
+    LOG_ERROR("%s lock not released, invalid fd", __func__);
     return BT_STATUS_PARM_INVALID;
   }
 
   ssize_t wrote_name_len = write(wake_unlock_fd, WAKE_LOCK_ID, locked_id_len);
   if (wrote_name_len == -1) {
-    LOG_ERROR(LOG_TAG, "%s can't release wake lock: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s can't release wake lock: %s", __func__, strerror(errno));
   } else if (wrote_name_len < locked_id_len) {
-    LOG_ERROR(LOG_TAG, "%s lock release only wrote %zd, assuming released",
-              __func__, wrote_name_len);
+    LOG_ERROR("%s lock release only wrote %zd, assuming released", __func__,
+              wrote_name_len);
   }
   return BT_STATUS_SUCCESS;
 }
@@ -191,13 +187,13 @@
 }
 
 static void wakelock_initialize_native(void) {
-  LOG_DEBUG(LOG_TAG, "%s opening wake locks", __func__);
+  LOG_DEBUG("%s opening wake locks", __func__);
 
   if (wake_lock_path.empty()) wake_lock_path = DEFAULT_WAKE_LOCK_PATH;
 
   wake_lock_fd = open(wake_lock_path.c_str(), O_RDWR | O_CLOEXEC);
   if (wake_lock_fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s can't open wake lock %s: %s", __func__,
+    LOG_ERROR("%s can't open wake lock %s: %s", __func__,
               wake_lock_path.c_str(), strerror(errno));
     CHECK(wake_lock_fd != INVALID_FD);
   }
@@ -206,7 +202,7 @@
 
   wake_unlock_fd = open(wake_unlock_path.c_str(), O_RDWR | O_CLOEXEC);
   if (wake_unlock_fd == INVALID_FD) {
-    LOG_ERROR(LOG_TAG, "%s can't open wake unlock %s: %s", __func__,
+    LOG_ERROR("%s can't open wake unlock %s: %s", __func__,
               wake_unlock_path.c_str(), strerror(errno));
     CHECK(wake_unlock_fd != INVALID_FD);
   }
@@ -214,7 +210,7 @@
 
 void wakelock_cleanup(void) {
   if (wakelock_stats.is_acquired) {
-    LOG_ERROR(LOG_TAG, "%s releasing wake lock as part of cleanup", __func__);
+    LOG_ERROR("%s releasing wake lock as part of cleanup", __func__);
     wakelock_release();
   }
   wake_lock_path.clear();
@@ -231,8 +227,7 @@
 static uint64_t now_ms(void) {
   struct timespec ts;
   if (clock_gettime(CLOCK_ID, &ts) == -1) {
-    LOG_ERROR(LOG_TAG, "%s unable to get current time: %s", __func__,
-              strerror(errno));
+    LOG_ERROR("%s unable to get current time: %s", __func__, strerror(errno));
     return 0;
   }
 
diff --git a/osi/test/config_test.cc b/osi/test/config_test.cc
index 32858fd..e16cd8a 100644
--- a/osi/test/config_test.cc
+++ b/osi/test/config_test.cc
@@ -1,13 +1,33 @@
-#include <base/files/file_util.h>
-#include <gtest/gtest.h>
-
-#include "AllocationTestHarness.h"
+/*
+ *  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.
+ */
 
 #include "osi/include/config.h"
 
-static const char CONFIG_FILE[] = "/data/local/tmp/config_test.conf";
+#include <base/files/file_util.h>
+#include <gtest/gtest.h>
+
+#include <filesystem>
+
+#include "AllocationTestHarness.h"
+
+static const std::filesystem::path kConfigFile =
+    std::filesystem::temp_directory_path() / "config_test.conf";
+static const char* CONFIG_FILE = kConfigFile.c_str();
 static const char CONFIG_FILE_CONTENT[] =
-    "                                                                                    \n\
+    "                                                                                \n\
 first_key=value                                                                      \n\
                                                                                      \n\
 # Device ID (DID) configuration                                                      \n\
@@ -55,11 +75,65 @@
   void SetUp() override {
     AllocationTestHarness::SetUp();
     FILE* fp = fopen(CONFIG_FILE, "wt");
-    fwrite(CONFIG_FILE_CONTENT, 1, sizeof(CONFIG_FILE_CONTENT), fp);
-    fclose(fp);
+    ASSERT_NE(fp, nullptr);
+    ASSERT_EQ(fwrite(CONFIG_FILE_CONTENT, 1, sizeof(CONFIG_FILE_CONTENT), fp),
+              sizeof(CONFIG_FILE_CONTENT));
+    ASSERT_EQ(fclose(fp), 0);
+  }
+
+  void TearDown() override {
+    EXPECT_TRUE(std::filesystem::remove(kConfigFile));
+    AllocationTestHarness::TearDown();
   }
 };
 
+TEST_F(ConfigTest, config_find) {
+  std::unique_ptr<config_t> config = config_new(CONFIG_FILE);
+  ASSERT_NE(config, nullptr);
+  EXPECT_TRUE(config->Has("DID"));
+  auto section_iter = config->Find("DID");
+  ASSERT_NE(section_iter, config->sections.end());
+  EXPECT_FALSE(config->Has("random"));
+  EXPECT_EQ(config->Find("random"), config->sections.end());
+}
+
+TEST_F(ConfigTest, section_find) {
+  std::unique_ptr<config_t> config = config_new(CONFIG_FILE);
+  ASSERT_NE(config, nullptr);
+  EXPECT_TRUE(config->Has("DID"));
+  auto section_iter = config->Find("DID");
+  ASSERT_NE(section_iter, config->sections.end());
+  EXPECT_EQ(section_iter->name, "DID");
+  EXPECT_TRUE(section_iter->Has("version"));
+  auto entry_iter = section_iter->Find("version");
+  ASSERT_NE(entry_iter, section_iter->entries.end());
+  EXPECT_EQ(entry_iter->key, "version");
+  EXPECT_EQ(entry_iter->value, "0x1436");
+  EXPECT_EQ(section_iter->Find("random"), section_iter->entries.end());
+  EXPECT_FALSE(section_iter->Has("random"));
+}
+
+TEST_F(ConfigTest, section_set) {
+  std::unique_ptr<config_t> config = config_new(CONFIG_FILE);
+  ASSERT_NE(config, nullptr);
+  EXPECT_TRUE(config->Has("DID"));
+  auto section_iter = config->Find("DID");
+  ASSERT_NE(section_iter, config->sections.end());
+  EXPECT_EQ(section_iter->name, "DID");
+  EXPECT_FALSE(section_iter->Has("random"));
+  section_iter->Set("random", "foo");
+  EXPECT_TRUE(section_iter->Has("random"));
+  auto entry_iter = section_iter->Find("random");
+  ASSERT_NE(entry_iter, section_iter->entries.end());
+  EXPECT_EQ(entry_iter->key, "random");
+  EXPECT_EQ(entry_iter->value, "foo");
+  section_iter->Set("random", "bar");
+  EXPECT_EQ(entry_iter->value, "bar");
+  entry_iter = section_iter->Find("random");
+  ASSERT_NE(entry_iter, section_iter->entries.end());
+  EXPECT_EQ(entry_iter->value, "bar");
+}
+
 TEST_F(ConfigTest, config_new_empty) {
   std::unique_ptr<config_t> config = config_new_empty();
   EXPECT_TRUE(config.get() != NULL);
@@ -176,22 +250,28 @@
 }
 
 TEST_F(ConfigTest, checksum_read) {
-  std::string filename = "/data/misc/bluedroid/test.checksum";
+  auto tmp_dir = std::filesystem::temp_directory_path();
+  auto filename = tmp_dir / "test.checksum";
   std::string checksum = "0x1234";
-  base::FilePath file_path(filename);
+  base::FilePath file_path(filename.string());
 
   EXPECT_EQ(base::WriteFile(file_path, checksum.data(), checksum.size()),
             (int)checksum.size());
 
   EXPECT_EQ(checksum_read(filename.c_str()), checksum.c_str());
+
+  EXPECT_TRUE(std::filesystem::remove(filename));
 }
 
 TEST_F(ConfigTest, checksum_save) {
-  std::string filename = "/data/misc/bluedroid/test.checksum";
+  auto tmp_dir = std::filesystem::temp_directory_path();
+  auto filename = tmp_dir / "test.checksum";
   std::string checksum = "0x1234";
-  base::FilePath file_path(filename);
+  base::FilePath file_path(filename.string());
 
   EXPECT_TRUE(checksum_save(checksum, filename));
 
   EXPECT_TRUE(base::PathExists(file_path));
+
+  EXPECT_TRUE(std::filesystem::remove(filename));
 }
diff --git a/osi/test/fuzzers/Android.bp b/osi/test/fuzzers/Android.bp
new file mode 100644
index 0000000..c39c0fd
--- /dev/null
+++ b/osi/test/fuzzers/Android.bp
@@ -0,0 +1,8 @@
+cc_defaults {
+    name: "libosi_fuzz_defaults",
+    defaults: ["fluoride_osi_defaults"],
+    host_supported: true,
+    static_libs: [
+        "libosi",
+    ],
+}
diff --git a/osi/test/fuzzers/alarm/Android.bp b/osi/test/fuzzers/alarm/Android.bp
new file mode 100644
index 0000000..51b5e560
--- /dev/null
+++ b/osi/test/fuzzers/alarm/Android.bp
@@ -0,0 +1,21 @@
+cc_fuzz {
+    name: "libosi_fuzz_alarm",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: false,
+    srcs: [
+        "fuzz_alarm.cc",
+    ],
+    shared_libs: [
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libcutils",
+        "libcrypto",
+    ],
+    static_libs: [
+        "libbt-common",
+        "libbt-protos-lite",
+        "libgmock",
+        "libosi",
+    ],
+    cflags: [ "-Wno-unused-function" ],
+}
diff --git a/osi/test/fuzzers/alarm/fuzz_alarm.cc b/osi/test/fuzzers/alarm/fuzz_alarm.cc
new file mode 100644
index 0000000..e4e6a25
--- /dev/null
+++ b/osi/test/fuzzers/alarm/fuzz_alarm.cc
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#include <fcntl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include "osi/include/alarm.h"
+#include "osi/include/semaphore.h"
+
+#include "common/message_loop_thread.h"
+
+using base::Closure;
+using base::TimeDelta;
+using bluetooth::common::MessageLoopThread;
+
+#define MAX_CONCURRENT_ALARMS 25
+#define MAX_BUFFER_LEN 4096
+#define MAX_ALARM_DURATION 25
+
+static semaphore_t* semaphore;
+static int cb_counter;
+static base::MessageLoop* message_loop_;
+
+base::MessageLoop* get_main_message_loop() { return message_loop_; }
+
+static void cb(void* data) {
+  ++cb_counter;
+  semaphore_post(semaphore);
+}
+
+void setup() {
+  cb_counter = 0;
+  semaphore = semaphore_new(0);
+}
+void teardown() { semaphore_free(semaphore); }
+
+alarm_t* fuzz_init_alarm(FuzzedDataProvider* dataProvider) {
+  size_t name_len =
+      dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUFFER_LEN);
+  std::vector<char> alarm_name_vect =
+      dataProvider->ConsumeBytesWithTerminator<char>(name_len, '\0');
+  char* alarm_name = alarm_name_vect.data();
+
+  // Determine if our alarm will be periodic
+  if (dataProvider->ConsumeBool()) {
+    return alarm_new_periodic(alarm_name);
+  } else {
+    return alarm_new(alarm_name);
+  }
+}
+
+bool fuzz_set_alarm(alarm_t* alarm, uint64_t interval, alarm_callback_t cb,
+                    FuzzedDataProvider* dataProvider) {
+  // Generate a random buffer (or null)
+  void* data_buffer = nullptr;
+  size_t buff_len =
+      dataProvider->ConsumeIntegralInRange<size_t>(1, MAX_BUFFER_LEN);
+  if (buff_len == 0) {
+    return false;
+  }
+
+  // allocate our space
+  std::vector<uint8_t> data_vector =
+      dataProvider->ConsumeBytes<uint8_t>(buff_len);
+  data_buffer = data_vector.data();
+
+  // Make sure alarm is non-null
+  if (alarm) {
+    // Should this alarm be regular or on mloop?
+    if (dataProvider->ConsumeBool()) {
+      alarm_set_on_mloop(alarm, interval, cb, data_buffer);
+    } else {
+      alarm_set(alarm, interval, cb, data_buffer);
+    }
+  }
+
+  return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  // Perform setup
+  setup();
+
+  alarm_t* alarm = nullptr;
+  // Should our alarm be valid or null?
+  if (dataProvider.ConsumeBool()) {
+    // Init our alarm
+    alarm = fuzz_init_alarm(&dataProvider);
+  }
+
+  // Set up the alarm & cancel
+  // Alarm must be non-null, or set() will trigger assert
+  if (alarm) {
+    if (!fuzz_set_alarm(alarm, MAX_ALARM_DURATION, cb, &dataProvider)) {
+      return 0;
+    }
+    alarm_cancel(alarm);
+  }
+
+  // Check if scheduled
+  alarm_is_scheduled(alarm);
+
+  if (alarm) {
+    // Set up another set of alarms & let these ones run
+    int num_alarms =
+        dataProvider.ConsumeIntegralInRange<uint8_t>(0, MAX_CONCURRENT_ALARMS);
+    for (int i = 0; i < num_alarms; i++) {
+      uint64_t interval =
+          dataProvider.ConsumeIntegralInRange<uint64_t>(0, MAX_ALARM_DURATION);
+      if (fuzz_set_alarm(alarm, interval, cb, &dataProvider)) {
+        return 0;
+      }
+      alarm_get_remaining_ms(alarm);
+    }
+
+    // Wait for them to complete
+    for (int i = 1; i <= num_alarms; i++) {
+      semaphore_wait(semaphore);
+    }
+  }
+
+  // Free the alarm object
+  alarm_free(alarm);
+
+  // dump debug data to /dev/null
+  int debug_fd = open("/dev/null", O_RDWR);
+  alarm_debug_dump(debug_fd);
+
+  // Cleanup
+  alarm_cleanup();
+
+  // Perform teardown
+  teardown();
+
+  return 0;
+}
diff --git a/osi/test/fuzzers/allocation_tracker/Android.bp b/osi/test/fuzzers/allocation_tracker/Android.bp
new file mode 100644
index 0000000..5f1e7dc
--- /dev/null
+++ b/osi/test/fuzzers/allocation_tracker/Android.bp
@@ -0,0 +1,17 @@
+cc_fuzz {
+    name: "libosi_fuzz_allocation_tracker",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: true,
+    srcs: [
+        "fuzz_allocation_tracker.cc",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    static_libs: [
+        "libosi",
+    ],
+    corpus: [
+        "corpus/checkfail-regression-156805580",
+    ],
+}
diff --git a/osi/test/fuzzers/allocation_tracker/corpus/checkfail-regression-156805580 b/osi/test/fuzzers/allocation_tracker/corpus/checkfail-regression-156805580
new file mode 100644
index 0000000..044c5b0
--- /dev/null
+++ b/osi/test/fuzzers/allocation_tracker/corpus/checkfail-regression-156805580
@@ -0,0 +1 @@
+þÿÿÿÿ¸
diff --git a/osi/test/fuzzers/allocation_tracker/fuzz_allocation_tracker.cc b/osi/test/fuzzers/allocation_tracker/fuzz_allocation_tracker.cc
new file mode 100644
index 0000000..966e87c
--- /dev/null
+++ b/osi/test/fuzzers/allocation_tracker/fuzz_allocation_tracker.cc
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "osi/include/allocation_tracker.h"
+
+#define MAX_NUM_FUNCTIONS 512
+#define MAX_BUF_SIZE 256
+
+// Add a tracker_initialized bool to track if we initialized or not
+// (This is to handle a call to allocation_tracker_notify_alloc immediately
+// returning the provided pointer if the allocator is not ready, and
+// notify_free on the same ptr failing as the allocator did not
+// track that allocation)
+bool tracker_initialized = false;
+
+struct alloc_struct {
+  allocator_id_t alloc_id;
+  void* ptr;
+};
+
+void freeAllocationVector(std::vector<alloc_struct>* alloc_vector) {
+  // Free our allocated buffers
+  for (const auto& alloc : *alloc_vector) {
+    void* real_ptr = allocation_tracker_notify_free(alloc.alloc_id, alloc.ptr);
+    if (real_ptr) {
+      free(real_ptr);
+    }
+  }
+  alloc_vector->clear();
+}
+
+void callArbitraryFunction(std::vector<alloc_struct>* alloc_vector,
+                           FuzzedDataProvider* dataProvider) {
+  // Get our function identifier
+  switch (dataProvider->ConsumeIntegralInRange<char>(0, 6)) {
+    // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer
+    // (This will likely bias whatever action is here to run more often)
+    case 0:
+      return;
+    // Init
+    case 1:
+      allocation_tracker_init();
+      tracker_initialized = true;
+      return;
+    case 2:
+      // NOTE: This will print to stderr if allocations exist. May clutter logs
+      allocation_tracker_expect_no_allocations();
+      return;
+    case 3: {
+      alloc_struct alloc;
+      // Determine allocator ID & buffer size (without canaries)
+      alloc.alloc_id = dataProvider->ConsumeIntegral<allocator_id_t>();
+      size_t size =
+          dataProvider->ConsumeIntegralInRange<size_t>(1, MAX_BUF_SIZE);
+      if (size == 0) {
+        return;
+      }
+      // Get our size with canaries & allocate
+      size_t real_size = allocation_tracker_resize_for_canary(size);
+      void* tmp_ptr = malloc(real_size);
+      if (tmp_ptr == nullptr) {
+        return;
+      }
+      alloc.ptr =
+          allocation_tracker_notify_alloc(alloc.alloc_id, tmp_ptr, size);
+      // Put our id/ptr pair in our tracking vector to be freed later
+      if (tracker_initialized && alloc.ptr) {
+        alloc_vector->push_back(alloc);
+      } else {
+        free(tmp_ptr);
+      }
+    }
+      return;
+    case 4: {
+      // Grab a ptr from our tracking vector & free it
+      if (!alloc_vector->empty()) {
+        size_t index = dataProvider->ConsumeIntegralInRange<size_t>(
+            0, alloc_vector->size() - 1);
+        alloc_struct alloc = alloc_vector->at(index);
+        void* real_ptr =
+            allocation_tracker_notify_free(alloc.alloc_id, alloc.ptr);
+        if (real_ptr) {
+          free(real_ptr);
+        }
+        alloc_vector->erase(alloc_vector->begin() + index);
+      }
+    }
+      return;
+    case 5: {
+      size_t size =
+          dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUF_SIZE);
+      allocation_tracker_resize_for_canary(size);
+    }
+      return;
+    // Reset
+    // NOTE: Should this be exempted from fuzzing? Header says to not call this,
+    //       but it's still exposed. It also doesn't perform a full reset.
+    case 6:
+      // Have to actually free the mem first as reset doesn't do it
+      freeAllocationVector(alloc_vector);
+      allocation_tracker_reset();
+      return;
+    default:
+      return;
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  // Keep a vector of our allocated pointers
+  std::vector<alloc_struct> alloc_vector;
+
+  // How many functions are we going to call?
+  size_t num_functions =
+      dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS);
+  for (size_t i = 0; i < num_functions; i++) {
+    callArbitraryFunction(&alloc_vector, &dataProvider);
+  }
+
+  // Free anything we've allocated over the course of the fuzzer loop
+  freeAllocationVector(&alloc_vector);
+
+  // Reset our tracker for the next run
+  allocation_tracker_reset();
+  return 0;
+}
diff --git a/osi/test/fuzzers/allocator/Android.bp b/osi/test/fuzzers/allocator/Android.bp
new file mode 100644
index 0000000..90db372
--- /dev/null
+++ b/osi/test/fuzzers/allocator/Android.bp
@@ -0,0 +1,11 @@
+cc_fuzz {
+    name: "libosi_fuzz_allocator",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: true,
+    srcs: [
+        "fuzz_allocator.cc",
+    ],
+    static_libs: [
+        "libosi",
+    ],
+}
diff --git a/osi/test/fuzzers/allocator/fuzz_allocator.cc b/osi/test/fuzzers/allocator/fuzz_allocator.cc
new file mode 100644
index 0000000..bdfd99a
--- /dev/null
+++ b/osi/test/fuzzers/allocator/fuzz_allocator.cc
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "osi/include/allocator.h"
+#include "osi/test/fuzzers/include/libosiFuzzHelperFunctions.h"
+
+#define MAX_NUM_FUNCTIONS 512
+#define MAX_BUF_SIZE 256
+
+void callArbitraryFunction(std::vector<void*>* alloc_vector,
+                           FuzzedDataProvider* dataProvider) {
+  // Get our function identifier
+  char func_id = dataProvider->ConsumeIntegralInRange<char>(0, 6);
+
+  switch (func_id) {
+    // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer
+    // (This will likely bias whatever action is here to run more often)
+    case 0:
+      return;
+    // Let case 1 be osi_malloc, and 2 be osi_calloc
+    case 1:
+    case 2: {
+      size_t size =
+          dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUF_SIZE);
+      void* ptr = nullptr;
+      if (size == 0) {
+        return;
+      }
+      if (func_id == 1) {
+        ptr = osi_malloc(size);
+      } else {
+        ptr = osi_calloc(size);
+      }
+      if (ptr) {
+        alloc_vector->push_back(ptr);
+      }
+    }
+      return;
+    // Let case 3 be osi_free, and 4 be osi_free_and_reset
+    case 3:
+    case 4: {
+      if (alloc_vector->size() == 0) {
+        return;
+      }
+      size_t index = dataProvider->ConsumeIntegralInRange<size_t>(
+          0, alloc_vector->size() - 1);
+      void* ptr = alloc_vector->at(index);
+      if (ptr) {
+        if (func_id == 3) {
+          osi_free(ptr);
+        } else {
+          osi_free_and_reset(&ptr);
+        }
+      }
+      alloc_vector->erase(alloc_vector->begin() + index);
+    }
+      return;
+    // Let case 5 be osi_strdup, and 6 be osi_strdup
+    case 5:
+    case 6: {
+      // Make a src buffer
+      char* buf = generateBuffer(dataProvider, MAX_BUF_SIZE, true);
+      char* str = nullptr;
+      if (buf == nullptr) {
+        return;
+      }
+      if (func_id == 5) {
+        str = osi_strdup(buf);
+      } else {
+        size_t size =
+            dataProvider->ConsumeIntegralInRange<size_t>(1, MAX_BUF_SIZE);
+        str = osi_strndup(buf, size);
+      }
+      free(buf);
+      if (str) {
+        alloc_vector->push_back(str);
+      }
+    }
+      return;
+    default:
+      return;
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  // Keep a vector of our allocated objects for freeing later
+  std::vector<void*> alloc_vector;
+  // Call some functions, create some buffers
+  size_t num_functions =
+      dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS);
+  for (size_t i = 0; i < num_functions; i++) {
+    callArbitraryFunction(&alloc_vector, &dataProvider);
+  }
+  // Free anything we've allocated
+  for (const auto& alloc : alloc_vector) {
+    if (alloc != nullptr) {
+      osi_free(alloc);
+    }
+  }
+  alloc_vector.clear();
+  return 0;
+}
diff --git a/osi/test/fuzzers/array/Android.bp b/osi/test/fuzzers/array/Android.bp
new file mode 100644
index 0000000..ffcaf8c
--- /dev/null
+++ b/osi/test/fuzzers/array/Android.bp
@@ -0,0 +1,14 @@
+cc_fuzz {
+    name: "libosi_fuzz_array",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: true,
+    srcs: [
+        "fuzz_array.cc",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    static_libs: [
+        "libosi",
+    ],
+}
diff --git a/osi/test/fuzzers/array/fuzz_array.cc b/osi/test/fuzzers/array/fuzz_array.cc
new file mode 100644
index 0000000..eeabf95
--- /dev/null
+++ b/osi/test/fuzzers/array/fuzz_array.cc
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "osi/include/array.h"
+
+// Capping the element size at sizeof(uint32_t)+1
+// because it looks like there's a buffer overread
+#define MAX_ELEMENT_SIZE sizeof(uint32_t)
+#define MAX_ARRAY_LEN 1024
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  // Attempt to init an array
+  size_t element_size =
+      dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_ELEMENT_SIZE);
+  array_t* arr = array_new(element_size);
+
+  // Functions can only be called on a non-null array_t, according to the .h
+  if (arr != nullptr) {
+    // How large do we want our array?
+    size_t arr_len =
+        dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_ARRAY_LEN);
+    if (arr_len > 0) {
+      for (size_t i = 0; i < arr_len; i++) {
+        uint32_t new_val = dataProvider.ConsumeIntegral<uint32_t>();
+        // append_value() just derefs and calls append_ptr(),
+        // so no need to fuzz separately
+        array_append_value(arr, new_val);
+      }
+
+      // Pull the ptr to an element in the array
+      size_t get_index =
+          dataProvider.ConsumeIntegralInRange<size_t>(0, array_length(arr) - 1);
+      array_at(arr, get_index);
+
+      // Grab the array pointer
+      array_ptr(arr);
+    }
+  }
+
+  // Free the array (this can be performed on a nullptr)
+  array_free(arr);
+
+  return 0;
+}
diff --git a/osi/test/fuzzers/buffer/Android.bp b/osi/test/fuzzers/buffer/Android.bp
new file mode 100644
index 0000000..3467e0d
--- /dev/null
+++ b/osi/test/fuzzers/buffer/Android.bp
@@ -0,0 +1,14 @@
+cc_fuzz {
+    name: "libosi_fuzz_buffer",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: true,
+    srcs: [
+        "fuzz_buffer.cc",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    static_libs: [
+        "libosi",
+    ],
+}
diff --git a/osi/test/fuzzers/buffer/fuzz_buffer.cc b/osi/test/fuzzers/buffer/fuzz_buffer.cc
new file mode 100644
index 0000000..b781a31
--- /dev/null
+++ b/osi/test/fuzzers/buffer/fuzz_buffer.cc
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "osi/include/buffer.h"
+
+#define MAX_BUFFER_SIZE 4096
+#define MAX_NUM_SLICES 100
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  // Create our buffer
+  size_t buf_size =
+      dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_BUFFER_SIZE);
+  buffer_t* buf = buffer_new(buf_size);
+
+  // These functions require a non-null buffer, according to the header
+  // The size also needs to be over 1 to make slices
+  if (buf != nullptr && buf_size > 1) {
+    std::vector<buffer_t*> slices;
+
+    // Make a bunch of refs to various slices of the buffer
+    size_t num_slices =
+        dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_SLICES);
+    for (size_t i = 0; i < num_slices; i++) {
+      // If slice_size is zero or GT buf_size, lib throws an exception
+      size_t slice_size =
+          dataProvider.ConsumeIntegralInRange<size_t>(1, buf_size - 1);
+      if (slice_size > 0) {
+        buffer_t* new_slice = nullptr;
+        if (slice_size == buf_size) {
+          new_slice = buffer_new_ref(buf);
+        } else {
+          new_slice = buffer_new_slice(buf, slice_size);
+        }
+
+        // Add the slice to our vector so we can free it later
+        slices.push_back(new_slice);
+      }
+    }
+
+    // Retrieve the buffer ptr
+    buffer_ptr(buf);
+
+    // Free the slices
+    for (const auto& slice : slices) {
+      buffer_free(slice);
+    }
+  }
+
+  // Free the root buffer
+  buffer_free(buf);
+
+  return 0;
+}
diff --git a/osi/test/fuzzers/compat/Android.bp b/osi/test/fuzzers/compat/Android.bp
new file mode 100644
index 0000000..3d9178b
--- /dev/null
+++ b/osi/test/fuzzers/compat/Android.bp
@@ -0,0 +1,15 @@
+cc_fuzz {
+    name: "libosi_fuzz_compat",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: true,
+    srcs: [
+        "fuzz_compat.cc",
+    ],
+    shared_libs: [
+        "liblog",
+        "libcutils",
+    ],
+    static_libs: [
+        "libosi",
+    ],
+}
diff --git a/osi/test/fuzzers/compat/fuzz_compat.cc b/osi/test/fuzzers/compat/fuzz_compat.cc
new file mode 100644
index 0000000..3feeaeb
--- /dev/null
+++ b/osi/test/fuzzers/compat/fuzz_compat.cc
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+#include <fuzzer/FuzzedDataProvider.h>
+#include "osi/include/compat.h"
+
+#define MAX_BUFFER_SIZE 4096
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+// Our functions are only defined with __GLIBC__
+#if __GLIBC__
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  size_t buf_size =
+      dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_BUFFER_SIZE);
+  if (buf_size == 0) {
+    return 0;
+  }
+
+  // Set up our buffers
+  // NOTE: If the src buffer is not NULL-terminated, the strlcpy will
+  //       overread regardless of the len arg. Force null-term for now.
+  std::vector<char> bytes =
+      dataProvider.ConsumeBytesWithTerminator<char>(buf_size, '\0');
+  if (bytes.empty()) {
+    return 0;
+  }
+  buf_size = bytes.size();
+  void* dst_buf = malloc(buf_size);
+  if (dst_buf == nullptr) {
+    return 0;
+  }
+
+  // Call the getId fn just to ensure things don't crash
+  gettid();
+
+  // Copy, then concat
+  size_t len_to_cpy = dataProvider.ConsumeIntegralInRange<size_t>(0, buf_size);
+  strlcpy(reinterpret_cast<char*>(dst_buf),
+          reinterpret_cast<char*>(bytes.data()), len_to_cpy);
+  strlcat(reinterpret_cast<char*>(dst_buf),
+          reinterpret_cast<char*>(bytes.data()), len_to_cpy);
+
+  // Clear out our dest buffer
+  free(dst_buf);
+#endif
+
+  return 0;
+}
diff --git a/osi/test/fuzzers/fixed_queue/Android.bp b/osi/test/fuzzers/fixed_queue/Android.bp
new file mode 100644
index 0000000..d21ee7c
--- /dev/null
+++ b/osi/test/fuzzers/fixed_queue/Android.bp
@@ -0,0 +1,15 @@
+cc_fuzz {
+    name: "libosi_fuzz_fixed_queue",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: true,
+    srcs: [
+        "fuzz_fixed_queue.cc",
+    ],
+    shared_libs: [
+        "liblog",
+        "libcutils",
+    ],
+    static_libs: [
+        "libosi",
+    ],
+}
diff --git a/osi/test/fuzzers/fixed_queue/fuzz_fixed_queue.cc b/osi/test/fuzzers/fixed_queue/fuzz_fixed_queue.cc
new file mode 100644
index 0000000..f8a426a
--- /dev/null
+++ b/osi/test/fuzzers/fixed_queue/fuzz_fixed_queue.cc
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <sys/select.h>
+#include "osi/include/fixed_queue.h"
+#include "osi/include/future.h"
+#include "osi/include/thread.h"
+#include "osi/test/fuzzers/include/libosiFuzzHelperFunctions.h"
+
+#define MAX_START_SIZE 2048
+#define MAX_NUM_FUNCTIONS 512
+#define MAX_BUF_SIZE 512
+
+static future_t* received_message_future = nullptr;
+
+// Empty callback function
+void fqFreeCb(void* data) {}
+void fqCb(fixed_queue_t* queue, void* data) {
+  void* msg = fixed_queue_try_dequeue(queue);
+  future_ready(received_message_future, msg);
+}
+
+// Returns either a nullptr or a function ptr to the placeholder cb function
+fixed_queue_free_cb cbOrNull(FuzzedDataProvider* dataProvider) {
+  bool null_cb = dataProvider->ConsumeBool();
+  if (null_cb) {
+    return nullptr;
+  } else {
+    return fqFreeCb;
+  }
+}
+
+bool fdIsAvailable(int fd) {
+  int nfds = 1;
+  fd_set readfds, writefds, exceptfds;
+  timeval timeout;
+
+  FD_ZERO(&readfds);
+  FD_ZERO(&writefds);
+  FD_ZERO(&exceptfds);
+  FD_SET(fd, &readfds);
+  timeout.tv_sec = 0;
+  timeout.tv_usec = 50;
+
+  return select(nfds, &readfds, &writefds, &exceptfds, &timeout) > 0;
+}
+
+void createNewFuture() {
+  // Free the existing future if it exists
+  if (received_message_future != nullptr) {
+    future_ready(received_message_future, nullptr);
+    future_await(received_message_future);
+  }
+
+  // Create a new one
+  received_message_future = future_new();
+}
+
+void callArbitraryFunction(fixed_queue_t* fixed_queue,
+                           std::vector<void*>* live_buffer_vector,
+                           std::vector<thread_t*>* live_thread_vector,
+                           FuzzedDataProvider* dataProvider) {
+  void* buf_ptr = nullptr;
+  size_t index = 0;
+  int fd = 0;
+  // Get our function identifier
+  switch (dataProvider->ConsumeIntegralInRange<char>(0, 17)) {
+    // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer
+    // (This will likely bias whatever action is here to run more often)
+    case 0:
+      return;
+    // Clear the queue
+    case 1:
+      fixed_queue_flush(fixed_queue, cbOrNull(dataProvider));
+      return;
+    // Check if empty
+    case 2:
+      fixed_queue_is_empty(fixed_queue);
+      return;
+    // Check length
+    case 3:
+      fixed_queue_length(fixed_queue);
+      return;
+    // Check capacity (Cannot be null)
+    case 4:
+      if (fixed_queue) {
+        fixed_queue_capacity(fixed_queue);
+      }
+      return;
+    // Add to the queue (Cannot be null)
+    case 5:
+      if (fixed_queue) {
+        buf_ptr = generateBuffer(dataProvider, MAX_BUF_SIZE, false);
+        live_buffer_vector->push_back(buf_ptr);
+        if (buf_ptr) {
+          // Make sure we won't block
+          fd = fixed_queue_get_enqueue_fd(fixed_queue);
+          if (fdIsAvailable(fd)) {
+            fixed_queue_enqueue(fixed_queue, buf_ptr);
+          }
+        }
+      }
+      return;
+    case 6:
+      if (fixed_queue) {
+        buf_ptr = generateBuffer(dataProvider, MAX_BUF_SIZE, false);
+        live_buffer_vector->push_back(buf_ptr);
+        if (buf_ptr) {
+          fixed_queue_try_enqueue(fixed_queue, buf_ptr);
+        }
+      }
+      return;
+    // Remove from the queue (Cannot be null)
+    case 7:
+      if (fixed_queue && fixed_queue_length(fixed_queue) > 0) {
+        fixed_queue_dequeue(fixed_queue);
+      }
+      return;
+    case 8:
+      if (fixed_queue) {
+        fixed_queue_try_dequeue(fixed_queue);
+      }
+      return;
+    // Peeks
+    case 9:
+      fixed_queue_try_peek_first(fixed_queue);
+      return;
+    case 10:
+      fixed_queue_try_peek_last(fixed_queue);
+      return;
+    // Try to remove existing specific element
+    case 11:
+      if (live_buffer_vector->empty()) {
+        return;
+      }
+      // Grab an existing buffer
+      index = dataProvider->ConsumeIntegralInRange<size_t>(
+          0, live_buffer_vector->size() - 1);
+      buf_ptr = live_buffer_vector->at(index);
+      if (buf_ptr != nullptr) {
+        fixed_queue_try_remove_from_queue(fixed_queue, buf_ptr);
+      }
+      return;
+    // Try to remove nonexistant element
+    case 12:
+      buf_ptr =
+          reinterpret_cast<void*>(dataProvider->ConsumeIntegral<uint64_t>());
+      if (buf_ptr != nullptr) {
+        fixed_queue_try_remove_from_queue(fixed_queue, buf_ptr);
+      }
+      return;
+    // Convert the queue to a list (Cannot be null)
+    case 13:
+      if (fixed_queue) {
+        fixed_queue_get_list(fixed_queue);
+      }
+      return;
+    // Check if enqueue is blocking
+    case 14:
+      fixed_queue_get_enqueue_fd(fixed_queue);
+      return;
+    // Check if dequeue is blocking
+    case 15:
+      fixed_queue_get_dequeue_fd(fixed_queue);
+      return;
+    // NOTE: thread appears to have a memleak, disabling this for now.
+    case 16:
+      // if (fixed_queue) {
+      //   createNewFuture();
+      //   // Start up a thread and register with it.
+      //   thread_t* tmp_thread = thread_new(
+      //       dataProvider->ConsumeRandomLengthString().c_str());
+      //   if (tmp_thread == nullptr) {
+      //     return;
+      //   }
+      //   live_thread_vector->push_back(tmp_thread);
+      //   reactor_t* reactor = thread_get_reactor(tmp_thread);
+      //   if (reactor == nullptr) {
+      //     return;
+      //   }
+      //   fixed_queue_register_dequeue(fixed_queue, reactor, fqCb, nullptr);
+      //   fixed_queue_enqueue(fixed_queue, (void*)"test");
+      //   future_await(received_message_future);
+      // }
+      return;
+    case 17:
+      fixed_queue_unregister_dequeue(fixed_queue);
+      return;
+    default:
+      return;
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  // Make vectors to keep track of objects we generate, for freeing
+  std::vector<void*> live_buffer_vector;
+  std::vector<thread_t*> live_thread_vector;
+
+  size_t start_capacity =
+      dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_START_SIZE);
+  fixed_queue_t* fixed_queue = fixed_queue_new(start_capacity);
+
+  // How many functions are we going to call?
+  size_t num_functions =
+      dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS);
+  for (size_t i = 0; i < num_functions; i++) {
+    callArbitraryFunction(fixed_queue, &live_buffer_vector, &live_thread_vector,
+                          &dataProvider);
+  }
+
+  // Free our queue (with either a null or placeholder callback)
+  fixed_queue_free(fixed_queue, cbOrNull(&dataProvider));
+
+  // Free buffers we've created through fn calls during this fuzzer loop.
+  for (const auto& buffer : live_buffer_vector) {
+    free(buffer);
+  }
+  for (const auto& thread : live_thread_vector) {
+    thread_free(thread);
+  }
+
+  return 0;
+}
diff --git a/osi/test/fuzzers/future/Android.bp b/osi/test/fuzzers/future/Android.bp
new file mode 100644
index 0000000..4fdbafe
--- /dev/null
+++ b/osi/test/fuzzers/future/Android.bp
@@ -0,0 +1,15 @@
+cc_fuzz {
+    name: "libosi_fuzz_future",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: true,
+    srcs: [
+        "fuzz_future.cc",
+    ],
+    shared_libs: [
+        "liblog",
+        "libcutils",
+    ],
+    static_libs: [
+        "libosi",
+    ],
+}
diff --git a/osi/test/fuzzers/future/fuzz_future.cc b/osi/test/fuzzers/future/fuzz_future.cc
new file mode 100644
index 0000000..1df3e7d
--- /dev/null
+++ b/osi/test/fuzzers/future/fuzz_future.cc
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "osi/include/future.h"
+
+#define MAX_BUFFER_SIZE 8
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  // The value of this result ptr shouldn't matter, but make a buffer to be safe
+  size_t buf_size =
+      dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_BUFFER_SIZE);
+  void* buf = malloc(buf_size);
+  if (buf == nullptr) {
+    return 0;
+  }
+  std::vector<uint8_t> bytes = dataProvider.ConsumeBytes<uint8_t>(buf_size);
+  memcpy(buf, bytes.data(), bytes.size());
+
+  // Is our future an immediate?
+  future_t* future = nullptr;
+  bool is_immediate = dataProvider.ConsumeBool();
+  if (is_immediate) {
+    future = future_new_immediate(buf);
+  } else {
+    future = future_new();
+  }
+
+  // These functions require a non-null object, according to the header
+  if (future != nullptr) {
+    // If we need to, specify that the future is ready
+    if (!is_immediate) {
+      future_ready(future, buf);
+    }
+
+    // Free the object
+    future_await(future);
+  }
+
+  free(buf);
+  return 0;
+}
diff --git a/osi/test/fuzzers/include/libosiFuzzHelperFunctions.h b/osi/test/fuzzers/include/libosiFuzzHelperFunctions.h
new file mode 100644
index 0000000..395b85e
--- /dev/null
+++ b/osi/test/fuzzers/include/libosiFuzzHelperFunctions.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBOSI_FUZZ_HELPERS_H_
+#define LIBOSI_FUZZ_HELPERS_H_
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <vector>
+
+char* generateBuffer(FuzzedDataProvider* dataProvider, size_t max_buffer_size,
+                     bool null_terminate) {
+  // Get our buffer size
+  size_t buf_size =
+      dataProvider->ConsumeIntegralInRange<size_t>(0, max_buffer_size);
+  if (buf_size == 0) {
+    return nullptr;
+  }
+
+  // Allocate and copy in data
+  char* buf = reinterpret_cast<char*>(malloc(buf_size));
+  std::vector<char> bytes = dataProvider->ConsumeBytes<char>(buf_size);
+  memcpy(buf, bytes.data(), bytes.size());
+
+  if (null_terminate) {
+    // Force a null-termination
+    buf[buf_size - 1] = 0x00;
+  }
+
+  return buf;
+}
+
+#endif  // LIBOSI_FUZZ_HELPERS_H_
diff --git a/osi/test/fuzzers/list/Android.bp b/osi/test/fuzzers/list/Android.bp
new file mode 100644
index 0000000..2b664bc
--- /dev/null
+++ b/osi/test/fuzzers/list/Android.bp
@@ -0,0 +1,11 @@
+cc_fuzz {
+    name: "libosi_fuzz_list",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: true,
+    srcs: [
+        "fuzz_list.cc",
+    ],
+    static_libs: [
+        "libosi",
+    ],
+}
diff --git a/osi/test/fuzzers/list/fuzz_list.cc b/osi/test/fuzzers/list/fuzz_list.cc
new file mode 100644
index 0000000..a581474
--- /dev/null
+++ b/osi/test/fuzzers/list/fuzz_list.cc
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "osi/include/list.h"
+#include "osi/test/fuzzers/include/libosiFuzzHelperFunctions.h"
+
+#define MAX_NUM_FUNCTIONS 512
+#define MAX_BUF_SIZE 256
+
+struct list_node_t {
+  struct list_node_t* next;
+  void* data;
+};
+
+void cb(void* data) {}
+// Pass a ptr to FuzzedDataProvider in context
+bool list_iter_cb_impl(void* data, void* context) {
+  FuzzedDataProvider* dataProvider =
+      reinterpret_cast<FuzzedDataProvider*>(context);
+  return dataProvider->ConsumeBool();
+}
+
+list_t* createList(FuzzedDataProvider* dataProvider) {
+  bool should_callback = dataProvider->ConsumeBool();
+  if (should_callback) {
+    return list_new(cb);
+  } else {
+    return list_new(nullptr);
+  }
+}
+
+void* getArbitraryElement(std::vector<void*>* vector,
+                          FuzzedDataProvider* dataProvider) {
+  if (vector->size() == 0) {
+    return nullptr;
+  }
+  // Get an index
+  size_t index =
+      dataProvider->ConsumeIntegralInRange<size_t>(0, vector->size() - 1);
+  return vector->at(index);
+}
+
+list_node_t* getArbitraryNode(list_t* list, FuzzedDataProvider* dataProvider) {
+  if (list == nullptr || list_is_empty(list)) {
+    return nullptr;
+  }
+  size_t index =
+      dataProvider->ConsumeIntegralInRange<size_t>(0, list_length(list) - 1);
+  list_node_t* node = list_begin(list);
+  for (size_t i = 0; i < index; i++) {
+    node = node->next;
+  }
+
+  return node;
+}
+
+void callArbitraryFunction(std::vector<void*>* list_vector,
+                           std::vector<void*>* alloc_vector,
+                           FuzzedDataProvider* dataProvider) {
+  list_t* list = nullptr;
+  // Get our function identifier
+  switch (dataProvider->ConsumeIntegralInRange<char>(0, 18)) {
+    // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer
+    // (This will likely bias whatever action is here to run more often)
+    case 0:
+      return;
+    // Create a new list
+    case 1:
+      list = createList(dataProvider);
+      list_vector->push_back(list);
+      return;
+    // Free a list
+    case 2: {
+      size_t index = 0;
+      if (list_vector->size() > 0) {
+        // Get an index
+        index = dataProvider->ConsumeIntegralInRange<size_t>(
+            0, list_vector->size() - 1);
+        list = reinterpret_cast<list_t*>(list_vector->at(index));
+      }
+      list_free(list);
+      // Otherwise free a valid list
+      if (list != nullptr) {
+        list_vector->erase(list_vector->begin() + index);
+      }
+      return;
+    }
+    case 3:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr) {
+        list_is_empty(list);
+      }
+      return;
+    case 4:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr) {
+        void* search_buf = getArbitraryElement(alloc_vector, dataProvider);
+        if (search_buf != nullptr) {
+          list_contains(list, search_buf);
+        }
+      }
+      return;
+    case 5:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr) {
+        list_length(list);
+      }
+      return;
+    case 6:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr && !list_is_empty(list)) {
+        list_front(list);
+      }
+      return;
+    case 7:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr && !list_is_empty(list)) {
+        list_back(list);
+      }
+      return;
+    case 8:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr && !list_is_empty(list)) {
+        list_back_node(list);
+      }
+      return;
+    case 9: {
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list == nullptr) {
+        return;
+      }
+      void* buf = generateBuffer(dataProvider, MAX_BUF_SIZE, false);
+      alloc_vector->push_back(buf);
+      list_node_t* node = getArbitraryNode(list, dataProvider);
+      if (node != nullptr && buf != nullptr) {
+        list_insert_after(list, node, buf);
+      }
+      return;
+    }
+    case 10: {
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      void* buf = generateBuffer(dataProvider, MAX_BUF_SIZE, false);
+      alloc_vector->push_back(buf);
+      if (list != nullptr && buf != nullptr) {
+        list_prepend(list, buf);
+      }
+      return;
+    }
+    case 11: {
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      void* buf = generateBuffer(dataProvider, MAX_BUF_SIZE, false);
+      alloc_vector->push_back(buf);
+      if (list != nullptr && buf != nullptr) {
+        list_append(list, buf);
+      }
+      return;
+    }
+    case 12: {
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      // The buffer will be valid, but may be for a different list
+      void* buf = getArbitraryElement(alloc_vector, dataProvider);
+      if (list != nullptr && buf != nullptr) {
+        list_remove(list, buf);
+      }
+      return;
+    }
+    case 13:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr) {
+        list_clear(list);
+      }
+      return;
+    case 14:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr) {
+        list_foreach(list, list_iter_cb_impl, dataProvider);
+      }
+      return;
+    case 15:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr) {
+        list_begin(list);
+      }
+      return;
+    case 16:
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list != nullptr) {
+        list_end(list);
+      }
+      return;
+    case 17: {
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list == nullptr) {
+        return;
+      }
+      list_node_t* node = getArbitraryNode(list, dataProvider);
+      if (node != nullptr) {
+        list_next(node);
+      }
+      return;
+    }
+    case 18: {
+      list = reinterpret_cast<list_t*>(
+          getArbitraryElement(list_vector, dataProvider));
+      if (list == nullptr) {
+        return;
+      }
+      list_node_t* node = getArbitraryNode(list, dataProvider);
+      if (node != nullptr && node != list_end(list)) {
+        list_node(node);
+      }
+      return;
+    }
+    default:
+      return;
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  // Keep a vector of our allocated objects for freeing later
+  std::vector<void*> list_vector;
+  std::vector<void*> alloc_vector;
+
+  // Call some functions, create some buffers
+  size_t num_functions =
+      dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS);
+  for (size_t i = 0; i < num_functions; i++) {
+    callArbitraryFunction(&list_vector, &alloc_vector, &dataProvider);
+  }
+
+  // Free anything we've allocated
+  for (const auto& list : list_vector) {
+    if (list != nullptr) {
+      list_free(reinterpret_cast<list_t*>(list));
+    }
+  }
+  for (const auto& alloc : alloc_vector) {
+    if (alloc != nullptr) {
+      free(alloc);
+    }
+  }
+  list_vector.clear();
+
+  return 0;
+}
diff --git a/osi/test/fuzzers/ringbuffer/Android.bp b/osi/test/fuzzers/ringbuffer/Android.bp
new file mode 100644
index 0000000..4343e02
--- /dev/null
+++ b/osi/test/fuzzers/ringbuffer/Android.bp
@@ -0,0 +1,11 @@
+cc_fuzz {
+    name: "libosi_fuzz_ringbuffer",
+    defaults: ["libosi_fuzz_defaults"],
+    host_supported: true,
+    srcs: [
+        "fuzz_ringbuffer.cc",
+    ],
+    static_libs: [
+        "libosi",
+    ],
+}
diff --git a/osi/test/fuzzers/ringbuffer/fuzz_ringbuffer.cc b/osi/test/fuzzers/ringbuffer/fuzz_ringbuffer.cc
new file mode 100644
index 0000000..adc219c
--- /dev/null
+++ b/osi/test/fuzzers/ringbuffer/fuzz_ringbuffer.cc
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "osi/include/ringbuffer.h"
+
+#define MAX_NUM_FUNCTIONS 512
+#define MAX_BUF_SIZE 2048
+
+ringbuffer_t* getArbitraryRingBuf(std::vector<ringbuffer_t*>* ringbuf_vector,
+                                  FuzzedDataProvider* dataProvider) {
+  if (ringbuf_vector->empty()) {
+    return nullptr;
+  }
+
+  size_t index = dataProvider->ConsumeIntegralInRange<size_t>(
+      0, ringbuf_vector->size() - 1);
+  return ringbuf_vector->at(index);
+}
+
+void callArbitraryFunction(std::vector<ringbuffer_t*>* ringbuf_vector,
+                           FuzzedDataProvider* dataProvider) {
+  // Get our function identifier
+  char func_id = dataProvider->ConsumeIntegralInRange<char>(0, 8);
+
+  ringbuffer_t* buf = nullptr;
+  switch (func_id) {
+    // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer
+    // (This will likely bias whatever action is here to run more often)
+    case 0:
+      return;
+    case 1: {
+      size_t size =
+          dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUF_SIZE);
+      buf = ringbuffer_init(size);
+      if (buf) {
+        ringbuf_vector->push_back(buf);
+      }
+    }
+      return;
+    case 2: {
+      if (ringbuf_vector->empty()) {
+        return;
+      }
+      size_t index = dataProvider->ConsumeIntegralInRange<size_t>(
+          0, ringbuf_vector->size() - 1);
+      buf = ringbuf_vector->at(index);
+      if (buf) {
+        ringbuffer_free(buf);
+        ringbuf_vector->erase(ringbuf_vector->begin() + index);
+      }
+    }
+      return;
+    case 3:
+      buf = getArbitraryRingBuf(ringbuf_vector, dataProvider);
+      if (buf) {
+        ringbuffer_available(buf);
+      }
+      return;
+    case 4:
+      buf = getArbitraryRingBuf(ringbuf_vector, dataProvider);
+      if (buf) {
+        ringbuffer_size(buf);
+      }
+      return;
+    case 5: {
+      buf = getArbitraryRingBuf(ringbuf_vector, dataProvider);
+      size_t size =
+          dataProvider->ConsumeIntegralInRange<size_t>(1, MAX_BUF_SIZE);
+      if (buf == nullptr || size == 0) {
+        return;
+      }
+      void* src_buf = malloc(size);
+      if (src_buf == nullptr) {
+        return;
+      }
+      std::vector<uint8_t> bytes = dataProvider->ConsumeBytes<uint8_t>(size);
+      memcpy(src_buf, bytes.data(), bytes.size());
+
+      ringbuffer_insert(buf, reinterpret_cast<uint8_t*>(src_buf), size);
+      free(src_buf);
+    }
+      return;
+    case 6:
+    case 7: {
+      buf = getArbitraryRingBuf(ringbuf_vector, dataProvider);
+      if (buf == nullptr) {
+        return;
+      }
+      size_t max_size = ringbuffer_size(buf);
+      if (max_size == 0) {
+        return;
+      }
+      size_t size = dataProvider->ConsumeIntegralInRange<size_t>(1, max_size);
+
+      // NOTE: 0-size may be a valid case, that crashes currently.
+      if (size == 0) {
+        return;
+      }
+
+      void* dst_buf = malloc(size);
+      if (dst_buf == nullptr) {
+        return;
+      }
+      if (func_id == 6) {
+        off_t offset = dataProvider->ConsumeIntegral<off_t>();
+        if (offset >= 0 &&
+            static_cast<size_t>(offset) <= ringbuffer_size(buf)) {
+          ringbuffer_peek(buf, offset, reinterpret_cast<uint8_t*>(dst_buf),
+                          size);
+        }
+      } else {
+        ringbuffer_pop(buf, reinterpret_cast<uint8_t*>(dst_buf), size);
+      }
+      free(dst_buf);
+    }
+      return;
+    case 8: {
+      buf = getArbitraryRingBuf(ringbuf_vector, dataProvider);
+      size_t size =
+          dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUF_SIZE);
+      if (buf) {
+        ringbuffer_delete(buf, size);
+      }
+    }
+      return;
+    default:
+      return;
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Init our wrapper
+  FuzzedDataProvider dataProvider(Data, Size);
+
+  // Keep a vector of our allocated objects for freeing later
+  std::vector<ringbuffer_t*> ringbuf_vector;
+
+  // Call some functions, create some buffers
+  size_t num_functions =
+      dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS);
+  for (size_t i = 0; i < num_functions; i++) {
+    callArbitraryFunction(&ringbuf_vector, &dataProvider);
+  }
+
+  // Free anything we've allocated
+  for (const auto& ringbuf : ringbuf_vector) {
+    if (ringbuf != nullptr) {
+      ringbuffer_free(ringbuf);
+    }
+  }
+  ringbuf_vector.clear();
+  return 0;
+}
diff --git a/packet/tests/fuzzers/Android.bp b/packet/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..53f6a44
--- /dev/null
+++ b/packet/tests/fuzzers/Android.bp
@@ -0,0 +1,748 @@
+cc_fuzz {
+    name: "avrcp_browse_packet_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+    srcs: [
+        "avrcp_browse_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/avrcp_browse_packets_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "change_path_req_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+    //system/core/base/include/android-base"
+    srcs: [
+        "change_path_req_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/change_path_req_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_capabilities_req_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_capabilities_req_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_capabilities_req_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_capabilities_res_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_capabilities_res_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_capabilities_res_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_item_attributes_req_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_item_attributes_req_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_item_attributes_req_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_play_status_req_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_play_status_req_fuzzer.cc",
+
+    ],
+
+    corpus: ["get_play_status_req_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_total_number_of_items_req_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_total_number_of_items_req_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_total_number_of_items_req_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "pass_through_packet_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "pass_through_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/pass_through_packet_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "play_item_packet_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "play_item_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/play_item_packet_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "register_notification_packet_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "register_notification_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/register_notification_packet_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "set_absolute_volume_packet_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "set_absolute_volume_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/set_absolute_volume_packet_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "set_addressed_player_packet_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "set_addressed_player_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/set_addressed_player_packet_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "set_browsed_player_req_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "set_browsed_player_req_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/set_browsed_player_req_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "vendor_packet_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "vendor_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/vendor_packet_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "avrcp_packet_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "avrcp_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/avrcp_packet_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "reject_packet_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "reject_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/reject_packet_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_element_attributes_req_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_element_attributes_req_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_element_attributes_req_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "change_path_res_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "change_path_res_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/change_path_res_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_element_attributes_res_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_element_attributes_res_packet_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_element_attributes_res_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_folder_items_res_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_folder_items_res_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_folder_items_res_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_folder_items_req_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_folder_items_req_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_folder_items_req_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_item_attributes_res_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_item_attributes_res_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_item_attributes_res_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_play_status_res_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_play_status_res_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_play_status_res_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "get_total_number_of_items_res_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "get_total_number_of_items_res_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/get_total_number_of_items_res_corpus/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
+
+cc_fuzz {
+    name: "set_browsed_player_res_fuzzer",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+
+    include_dirs: [
+        "system/bt/",
+        "system/bt/include",
+        "system/bt/packet/include",
+        "system/bt/packet/tests",
+        "system/bt/packet/tests/avrcp",
+        "system/bt/packet/base",
+    ],
+
+    srcs: [
+        "set_browsed_player_res_fuzzer.cc",
+
+    ],
+
+    corpus: ["corpus/set_browsed_player_res_fuzzer/*"],
+
+    static_libs: [
+        "libgmock",
+        "lib-bt-packets",
+    ],
+    cflags: [
+        "-DBUILDCFG",
+    ],
+}
diff --git a/packet/tests/fuzzers/avrcp_browse_packet_fuzzer.cc b/packet/tests/fuzzers/avrcp_browse_packet_fuzzer.cc
new file mode 100644
index 0000000..f2b84b3
--- /dev/null
+++ b/packet/tests/fuzzers/avrcp_browse_packet_fuzzer.cc
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+// Adapted from AVRCP Browse Packet Test
+
+#include <gtest/gtest.h>
+
+#include "avrcp_browse_packet.h"
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+
+namespace avrcp {
+
+using TestBrowsePacket = TestPacketType<BrowsePacket>;
+
+// buildpacket test
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> get_folder_items_request;
+
+  // Expected packet size by the library is ~8
+  if (size > 10) {
+    for (size_t x = 0; x < size; x++) {
+      get_folder_items_request.push_back(data[x]);
+    }
+
+    auto test_packet = TestBrowsePacket::Make(get_folder_items_request);
+
+    test_packet->GetPdu();
+    test_packet->GetLength();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/avrcp_packet_fuzzer.cc b/packet/tests/fuzzers/avrcp_packet_fuzzer.cc
new file mode 100644
index 0000000..a63ac70
--- /dev/null
+++ b/packet/tests/fuzzers/avrcp_packet_fuzzer.cc
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+// Adapted from avrcp_packet_test.cc
+#include <stddef.h>
+#include <stdint.h>
+#include "avrcp_packet.h"
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+
+// A helper class that has public accessors to protected methods
+class TestPacketBuilder : public PacketBuilder {
+ public:
+  static std::unique_ptr<TestPacketBuilder> MakeBuilder(
+      std::vector<uint8_t> data) {
+    std::unique_ptr<TestPacketBuilder> builder(new TestPacketBuilder(data));
+    return builder;
+  }
+
+  // Make all the utility functions public
+  using PacketBuilder::AddPayloadOctets1;
+  using PacketBuilder::AddPayloadOctets2;
+  using PacketBuilder::AddPayloadOctets3;
+  using PacketBuilder::AddPayloadOctets4;
+  using PacketBuilder::AddPayloadOctets6;
+  using PacketBuilder::AddPayloadOctets8;
+  using PacketBuilder::ReserveSpace;
+
+  size_t size() const override { return data_.size(); };
+
+  bool Serialize(const std::shared_ptr<Packet>& pkt) override {
+    ReserveSpace(pkt, size());
+
+    for (uint8_t byte : data_) {
+      AddPayloadOctets1(pkt, byte);
+    }
+
+    return true;
+  }
+
+  explicit TestPacketBuilder(std::vector<uint8_t> data) : data_(data) {}
+
+  std::vector<uint8_t> data_;
+};
+
+namespace avrcp {
+
+using TestAvrcpPacket = TestPacketType<Packet>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> get_capabilities_request_payload;
+
+  if (size >= 8) {
+    get_capabilities_request.push_back(0);
+    for (int x = 0; x < 6; x++) {
+      get_capabilities_request_payload.push_back(data[x]);
+    }
+
+    auto cap_req_builder =
+        TestPacketBuilder::MakeBuilder(get_capabilities_request_payload);
+
+    auto builder = PacketBuilder::MakeBuilder(
+        CType::STATUS, 0x09, 0x00, Opcode::VENDOR, std::move(cap_req_builder));
+
+    auto test_packet = TestAvrcpPacket::Make();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/change_path_req_fuzzer.cc b/packet/tests/fuzzers/change_path_req_fuzzer.cc
new file mode 100644
index 0000000..8ca88b2
--- /dev/null
+++ b/packet/tests/fuzzers/change_path_req_fuzzer.cc
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+// Adapted from change_path_packet_test.cc
+
+#include <base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "change_path.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestChangePathReqPacket = TestPacketType<ChangePathRequest>;
+
+// Getter
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> change_path_request_data;
+
+  // Minimum for this type of packet appears to be 14 bytes.
+  if (size > 14) {
+    for (size_t x = 0; x < size; x++) {
+      change_path_request_data.push_back(data[x]);
+    }
+
+    auto test_packet = TestChangePathReqPacket::Make(change_path_request_data);
+
+    test_packet->GetUidCounter();
+    test_packet->GetDirection();
+    test_packet->GetUid();
+    test_packet->IsValid();
+    test_packet->ToString();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/change_path_res_fuzzer.cc b/packet/tests/fuzzers/change_path_res_fuzzer.cc
new file mode 100644
index 0000000..127ff07
--- /dev/null
+++ b/packet/tests/fuzzers/change_path_res_fuzzer.cc
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+// Adapted from change_path_packet_test.cc
+
+#include <base/logging.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include "avrcp_test_packets.h"
+#include "change_path.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestChangePathReqPacket = TestPacketType<ChangePathRequest>;
+
+// Getter
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+  auto builder = ChangePathResponseBuilder::MakeBuilder(
+      Status::NO_ERROR, data_provider.ConsumeIntegral<uint32_t>());
+
+  auto test_packet = TestChangePathReqPacket::Make();
+  builder->Serialize(test_packet);
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/corpus/avrcp_browse_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/avrcp_browse_packet_corpus/validpacket
new file mode 100644
index 0000000..1fa87a8
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/avrcp_browse_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/avrcp_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/avrcp_packet_corpus/validpacket
new file mode 100644
index 0000000..c7c5b33
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/avrcp_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/avrcp_test_packets_corpus/validpacket b/packet/tests/fuzzers/corpus/avrcp_test_packets_corpus/validpacket
new file mode 100644
index 0000000..581327a
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/avrcp_test_packets_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/change_path_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/change_path_packet_corpus/validpacket
new file mode 100644
index 0000000..581327a
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/change_path_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/generate_corpus.sh b/packet/tests/fuzzers/corpus/generate_corpus.sh
new file mode 100644
index 0000000..9548575
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/generate_corpus.sh
@@ -0,0 +1,52 @@
+mkdir avrcp_browse_packet_corpus
+mkdir change_path_req_corpus
+mkdir get_capabilities_req_corpus
+mkdir get_item_attributes_req_corpus
+mkdir get_play_status_req_corpus
+mkdir get_total_number_of_items_req_corpus
+mkdir pass_through_packet_corpus
+mkdir play_item_packet_corpus
+mkdir register_notification_packet_corpus
+mkdir set_absolute_volume_packet_corpus
+mkdir set_addressed_player_packet_corpus
+mkdir set_browsed_player_req_corpus
+mkdir vendor_packet_corpus
+mkdir avrcp_packet_corpus
+mkdir reject_packet_corpus
+#New ones
+mkdir change_path_res_corpus
+mkdir get_element_attributes_req_packet_corpus
+mkdir get_element_attributes_res_packet_corpus
+mkdir get_folder_items_res_corpus
+mkdir get_folder_items_req_corpus
+mkdir get_item_attributes_res_corpus
+mkdir get_play_status_res_corpus
+mkdir get_total_number_of_items_res_corpus
+mkdir set_browsed_player_res_corpus
+
+echo -n -e '\x71,\x00,\x0a,\x00,\x00,\x00,\x00,\x00,\x00,\x00,\x00,\x03,\x00' > avrcp_browse_packet_corpus/validpacket
+echo -n -e '\x72\x00\x0b\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02' > change_path_packet_corpus/validpacket
+echo -n -e '\x72\x00\x0b\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02' > get_capabilities_req_corpus/validpacket
+echo -n -e '\x73\x00\x28\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x07\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07' > get_item_attributes_req_corpus/validpacket 
+echo -n -e '\x01\x48\x00\x00\x19\x58\x30\x00\x00\x00' > get_play_status_req_corpus/validpacket
+echo -n -e '\x75\x00\x00' > get_total_number_of_items_req_corpus/validpacket
+echo -n -e '\x00\x48\x7c\x44\x00' > pass_through_packet_corpus/validpacket
+echo -n -e '\x00\x48\x00\x00\x19\x58\x74\x00\x00\x0b\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00' > play_item_req_corpus/validpacket
+echo -n -e '\x03\x48\x00\x00\x19\x58\x31\x00\x00\x04\x00\x00\x00\x00' > register_notification_packet_corpus/validpacket
+echo -n -e '\x00\x48\x00\x00\x19\x58\x50\x00\x00\x01\x48' > set_absolute_volume_packet_corpus/validpacket
+echo -n -e '\x00\x48\x00\x00\x19\x58\x60\x00\x00\x00' > set_addressed_player_packet_corpus/validpacket
+echo -n -e '\x70\x00\x02\x00\x02' > set_browsed_player_req_corpus/validpacket
+echo -n -e '\x01\x48\x00\x00\x19\x58\x10\x00\x00\x01' > vendor_packet_corpus/validpacket
+echo -n -e '\x01\x48\x00\x00\x19\x58\x10\x00\x00\x00' > avrcp_packet_corpus/validpacket
+
+#new ones 
+
+echo -n -e '0x01, 0x48, 0x00, 0x00, 0x19, 0x58, 0x20, 0x00, 0x00, 0x00' > get_element_attributes_req_packet_corpus/validpacket
+echo -n -e '0x01, 0x48, 0x00, 0x00, 0x19, 0x58, 0x20, 0x00, 0x00, 0x00' > get_element_attributes_res_packet_corpus/validpacket
+echo -n -e '0x01, 0x48, 0x00, 0x00, 0x19, 0x58, 0x20, 0x00, 0x00, 0x00' > get_folder_items_res_corpus/validpacket
+echo -n -e '0x01, 0x48, 0x00, 0x00, 0x19, 0x58, 0x20, 0x00, 0x00, 0x00' > get_folder_items_req_corpus/validpacket
+echo -n -e '0x01, 0x48, 0x00, 0x00, 0x19, 0x58, 0x20, 0x00, 0x00, 0x00' > get_item_attributes_res_corpus/validpacket
+echo -n -e '0x01, 0x48, 0x00, 0x00, 0x19, 0x58, 0x20, 0x00, 0x00, 0x00' > get_play_status_res_corpus/validpacket
+echo -n -e '0x01, 0x48, 0x00, 0x00, 0x19, 0x58, 0x20, 0x00, 0x00, 0x00' > get_total_number_of_items_res_corpus/validpacket
+echo -n -e '0x01, 0x48, 0x00, 0x00, 0x19, 0x58, 0x20, 0x00, 0x00, 0x00' > set_browsed_player_res_corpus/validpacket
+echo -n -e '0x01, 0x48, 0x00, 0x00, 0x19, 0x58, 0x20, 0x00, 0x00, 0x00' > change_path_res_corpus/validpacket
\ No newline at end of file
diff --git a/packet/tests/fuzzers/corpus/get_capabilities_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/get_capabilities_packet_corpus/validpacket
new file mode 100644
index 0000000..581327a
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/get_capabilities_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/get_item_attributes_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/get_item_attributes_packet_corpus/validpacket
new file mode 100644
index 0000000..7d706b8
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/get_item_attributes_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/get_play_status_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/get_play_status_packet_corpus/validpacket
new file mode 100644
index 0000000..2311a47
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/get_play_status_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/get_total_number_of_items_corpus/validpacket b/packet/tests/fuzzers/corpus/get_total_number_of_items_corpus/validpacket
new file mode 100644
index 0000000..bc634e2
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/get_total_number_of_items_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/pass_through_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/pass_through_packet_corpus/validpacket
new file mode 100644
index 0000000..9e002d2
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/pass_through_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/play_item_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/play_item_packet_corpus/validpacket
new file mode 100644
index 0000000..538a7c0
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/play_item_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/register_notification_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/register_notification_packet_corpus/validpacket
new file mode 100644
index 0000000..2a213f0
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/register_notification_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/set_absolute_volume_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/set_absolute_volume_packet_corpus/validpacket
new file mode 100644
index 0000000..5403b84
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/set_absolute_volume_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/set_addressed_player_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/set_addressed_player_packet_corpus/validpacket
new file mode 100644
index 0000000..4872d6b
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/set_addressed_player_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/set_browsed_player_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/set_browsed_player_packet_corpus/validpacket
new file mode 100644
index 0000000..667137b
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/set_browsed_player_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/corpus/vendor_packet_corpus/validpacket b/packet/tests/fuzzers/corpus/vendor_packet_corpus/validpacket
new file mode 100644
index 0000000..0c2a66e
--- /dev/null
+++ b/packet/tests/fuzzers/corpus/vendor_packet_corpus/validpacket
Binary files differ
diff --git a/packet/tests/fuzzers/get_capabilities_req_fuzzer.cc b/packet/tests/fuzzers/get_capabilities_req_fuzzer.cc
new file mode 100644
index 0000000..1243d0c
--- /dev/null
+++ b/packet/tests/fuzzers/get_capabilities_req_fuzzer.cc
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+// Adapted from get_capabilities_packet_test
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "capabilities_packet.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using GetCapRequestTestPacket = TestPacketType<GetCapabilitiesRequest>;
+
+// Test parsing a GetCapabilities Request
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> get_capabilities_request;
+  // We will use stability testing to see the sensible max size for fuzzing.
+  // Max BT packet size = 251
+  // Expected packet size by the library is ~ 10
+  if (size >= 12) {
+    get_capabilities_request.push_back(0);
+    for (size_t x = 0; x < size; x++) {
+      get_capabilities_request.push_back(data[x]);
+    }
+
+    auto test_packet = GetCapRequestTestPacket::Make(get_capabilities_request);
+    test_packet->GetCapabilityRequested();
+    test_packet->IsValid();
+    test_packet->ToString();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_capabilities_res_fuzzer.cc b/packet/tests/fuzzers/get_capabilities_res_fuzzer.cc
new file mode 100644
index 0000000..a01cda1
--- /dev/null
+++ b/packet/tests/fuzzers/get_capabilities_res_fuzzer.cc
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "avrcp_test_packets.h"
+#include "capabilities_packet.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+// Test parsing a GetCapabilities Request
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+  auto builder = GetCapabilitiesResponseBuilder::MakeCompanyIdBuilder(
+      data_provider.ConsumeIntegral<uint32_t>());
+  builder->AddCompanyId(data_provider.ConsumeIntegral<uint32_t>());
+  builder->AddCompanyId(data_provider.ConsumeIntegral<uint32_t>());
+
+  builder = GetCapabilitiesResponseBuilder::MakeEventsSupportedBuilder(
+      Event::PLAYBACK_STATUS_CHANGED);
+  builder->AddEvent(Event::TRACK_CHANGED);
+  builder->AddEvent(Event::PLAYBACK_POS_CHANGED);
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_element_attributes_req_packet_fuzzer.cc b/packet/tests/fuzzers/get_element_attributes_req_packet_fuzzer.cc
new file mode 100644
index 0000000..68c038f
--- /dev/null
+++ b/packet/tests/fuzzers/get_element_attributes_req_packet_fuzzer.cc
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "get_element_attributes_packet.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetElemAttrReqPacket = TestPacketType<GetElementAttributesRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> get_element_attributes_request_full;
+
+  // Expected packet size by the library is ~10
+  if (size >= 10) {
+    get_element_attributes_request_full.push_back(0);
+    for (size_t x = 0; x < size; x++) {
+      get_element_attributes_request_full.push_back(data[x]);
+    }
+
+    auto test_packet =
+        TestGetElemAttrReqPacket::Make(get_element_attributes_request_full);
+    if (!test_packet->IsValid()) return 0;
+    test_packet->GetIdentifier();
+    auto tpString = test_packet->ToString();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_element_attributes_res_packet_fuzzer.cc b/packet/tests/fuzzers/get_element_attributes_res_packet_fuzzer.cc
new file mode 100644
index 0000000..fa90eaa
--- /dev/null
+++ b/packet/tests/fuzzers/get_element_attributes_res_packet_fuzzer.cc
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "get_element_attributes_packet.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetElemAttrReqPacket = TestPacketType<GetElementAttributesRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+  auto builder = GetElementAttributesResponseBuilder::MakeBuilder(0xFFFF);
+  FuzzedDataProvider data_provider(data, size);
+  std::string s = data_provider.ConsumeRemainingBytesAsString();
+  builder->AddAttributeEntry(Attribute::TITLE, s);
+  builder->AddAttributeEntry(Attribute::ARTIST_NAME, s);
+  builder->AddAttributeEntry(Attribute::ALBUM_NAME, s);
+  builder->AddAttributeEntry(Attribute::TRACK_NUMBER, s);
+  builder->AddAttributeEntry(Attribute::TOTAL_NUMBER_OF_TRACKS, s);
+  builder->AddAttributeEntry(Attribute::GENRE, s);
+  builder->AddAttributeEntry(Attribute::PLAYING_TIME, s);
+
+  auto test_packet = TestGetElemAttrReqPacket::Make();
+  builder->Serialize(test_packet);
+  test_packet->GetData();
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_folder_items_req_fuzzer.cc b/packet/tests/fuzzers/get_folder_items_req_fuzzer.cc
new file mode 100644
index 0000000..fe2478c
--- /dev/null
+++ b/packet/tests/fuzzers/get_folder_items_req_fuzzer.cc
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+// Adapted from get_folder_items_packet_test
+
+#include "avrcp_test_packets.h"
+#include "get_folder_items.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetFolderItemsReqPacket = TestPacketType<GetFolderItemsRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> get_folder_items_request_vfs;
+
+  // Starts large to avoid throwing exceptions
+  if (size >= 34) {
+    for (size_t x = 0; x < size; x++) {
+      get_folder_items_request_vfs.push_back(data[x]);
+    }
+
+    auto test_packet =
+        TestGetFolderItemsReqPacket::Make(get_folder_items_request_vfs);
+
+    test_packet->GetScope();
+    test_packet->GetStartItem();
+    test_packet->GetEndItem();
+    test_packet->GetNumAttributes();
+    test_packet->IsValid();
+    test_packet->ToString();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_folder_items_res_fuzzer.cc b/packet/tests/fuzzers/get_folder_items_res_fuzzer.cc
new file mode 100644
index 0000000..09e5252
--- /dev/null
+++ b/packet/tests/fuzzers/get_folder_items_res_fuzzer.cc
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+// Adapted from get_folder_items_packet_test
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "avrcp_test_packets.h"
+#include "get_folder_items.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetFolderItemsReqPacket = TestPacketType<GetFolderItemsRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+  auto builder = GetFolderItemsResponseBuilder::MakeNowPlayingBuilder(
+      Status::NO_ERROR, 0x0000, 0xFFFF);
+  std::set<AttributeEntry> attributes;
+  FuzzedDataProvider data_provider(data, size);
+  std::string s = data_provider.ConsumeRemainingBytesAsString();
+  attributes.insert(AttributeEntry(Attribute::TITLE, s));
+  auto song = MediaElementItem(0x02, s, attributes);
+  builder->AddSong(song);
+
+  auto test_packet = TestGetFolderItemsReqPacket::Make();
+  builder->Serialize(test_packet);
+  test_packet->GetData();
+
+  // Second test with the same data.
+  auto builder2 = GetFolderItemsResponseBuilder::MakeVFSBuilder(
+      Status::NO_ERROR, 0x0000, 0xFFFF);
+  auto folder = FolderItem(0x0000000000000001, 0x00, true, s);
+  builder2->AddFolder(folder);
+
+  test_packet = TestGetFolderItemsReqPacket::Make();
+  builder2->Serialize(test_packet);
+  test_packet->GetData();
+
+  // Third test with the same data.
+  MediaPlayerItem player1(1, s, true);
+
+  // Browsing Header + Status field + UID Counter field + Number of Items
+  // field
+  auto packet_size = BrowsePacket::kMinSize() + 5;
+  packet_size += player1.size();
+
+  auto builder3 = GetFolderItemsResponseBuilder::MakePlayerListBuilder(
+      Status::NO_ERROR, 0x0000, packet_size);
+
+  builder3->AddMediaPlayer(player1);
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_item_attributes_req_fuzzer.cc b/packet/tests/fuzzers/get_item_attributes_req_fuzzer.cc
new file mode 100644
index 0000000..88fdcd3
--- /dev/null
+++ b/packet/tests/fuzzers/get_item_attributes_req_fuzzer.cc
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+// Adapted from get_item_attributes_packet_test
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "get_item_attributes.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetItemAttrsReqPacket = TestPacketType<GetItemAttributesRequest>;
+
+// Builder packet.
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> get_item_attributes_request;
+
+  if (size > 44) {
+    for (size_t x = 0; x < size; x++) {
+      get_item_attributes_request.push_back(data[x]);
+    }
+    auto test_packet =
+        TestGetItemAttrsReqPacket::Make(get_item_attributes_request);
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_item_attributes_res_fuzzer.cc b/packet/tests/fuzzers/get_item_attributes_res_fuzzer.cc
new file mode 100644
index 0000000..ca50fb5
--- /dev/null
+++ b/packet/tests/fuzzers/get_item_attributes_res_fuzzer.cc
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+// Adapted from get_item_attributes_packet_test
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "get_item_attributes.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetItemAttrsReqPacket = TestPacketType<GetItemAttributesRequest>;
+
+// Builder packet.
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+  std::string s = data_provider.ConsumeRemainingBytesAsString();
+  auto builder =
+      GetItemAttributesResponseBuilder::MakeBuilder(Status::NO_ERROR, 0xFFFF);
+  builder->AddAttributeEntry(Attribute::TITLE, s);
+  builder->AddAttributeEntry(Attribute::ARTIST_NAME, s);
+  builder->AddAttributeEntry(Attribute::ALBUM_NAME, s);
+
+  auto test_packet = TestGetItemAttrsReqPacket::Make();
+  builder->Serialize(test_packet);
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_play_status_req_fuzzer.cc b/packet/tests/fuzzers/get_play_status_req_fuzzer.cc
new file mode 100644
index 0000000..25c549e
--- /dev/null
+++ b/packet/tests/fuzzers/get_play_status_req_fuzzer.cc
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+// Adapted from get_play_status_packet_test.cc
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include "avrcp_test_packets.h"
+#include "get_play_status_packet.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetPlayStatusRspPacket = TestPacketType<Packet>;
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+
+  auto builder = GetPlayStatusResponseBuilder::MakeBuilder(
+      0, data_provider.ConsumeIntegral<uint32_t>(), 0);
+  auto test_packet = TestGetPlayStatusRspPacket::Make();
+  builder->Serialize(test_packet);
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_play_status_res_fuzzer.cc b/packet/tests/fuzzers/get_play_status_res_fuzzer.cc
new file mode 100644
index 0000000..cfadfb3
--- /dev/null
+++ b/packet/tests/fuzzers/get_play_status_res_fuzzer.cc
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+// Adapted from get_play_status_packet_test.cc
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "get_play_status_packet.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetPlayStatusRspPacket = TestPacketType<Packet>;
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+  auto builder = GetPlayStatusResponseBuilder::MakeBuilder(
+      0, data_provider.ConsumeIntegral<uint32_t>(), 0);
+  auto test_packet = TestGetPlayStatusRspPacket::Make();
+  builder->Serialize(test_packet);
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_total_number_of_items_req_fuzzer.cc b/packet/tests/fuzzers/get_total_number_of_items_req_fuzzer.cc
new file mode 100644
index 0000000..ef290b0
--- /dev/null
+++ b/packet/tests/fuzzers/get_total_number_of_items_req_fuzzer.cc
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+// Adapted from get_total_number_of_items_test
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "get_total_number_of_items.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetTotalNumItemsReqPacket =
+    TestPacketType<GetTotalNumberOfItemsRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> get_total_number_of_items_request_now_playing;
+
+  // Expected packet size by the library is ~4
+  if (size >= 8) {
+    for (size_t x = 0; x < size; x++) {
+      get_total_number_of_items_request_now_playing.push_back(data[x]);
+    }
+
+    auto test_packet = TestGetTotalNumItemsReqPacket::Make(
+        get_total_number_of_items_request_now_playing);
+    test_packet->GetScope();
+    test_packet->IsValid();
+    test_packet->GetData();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/get_total_number_of_items_res_fuzzer.cc b/packet/tests/fuzzers/get_total_number_of_items_res_fuzzer.cc
new file mode 100644
index 0000000..b456d32
--- /dev/null
+++ b/packet/tests/fuzzers/get_total_number_of_items_res_fuzzer.cc
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+// Adapted from get_total_number_of_items_test
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gtest/gtest.h>
+#include <stdlib.h>
+
+#include "avrcp_test_packets.h"
+#include "get_total_number_of_items.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestGetTotalNumItemsReqPacket =
+    TestPacketType<GetTotalNumberOfItemsRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+  auto builder = GetTotalNumberOfItemsResponseBuilder::MakeBuilder(
+      Status::NO_ERROR, data_provider.ConsumeIntegral<uint32_t>(),
+      data_provider.ConsumeIntegral<uint32_t>());
+
+  auto test_packet = TestGetTotalNumItemsReqPacket::Make();
+  builder->Serialize(test_packet);
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/pass_through_packet_fuzzer.cc b/packet/tests/fuzzers/pass_through_packet_fuzzer.cc
new file mode 100644
index 0000000..52b48bf
--- /dev/null
+++ b/packet/tests/fuzzers/pass_through_packet_fuzzer.cc
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+// Adapted from pass_through_packet_fuzzer.cc
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+#include "pass_through_packet.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestPassThroughPacket = TestPacketType<PassThroughPacket>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> pass_through_command_play_pushed;
+
+  // Expected packet size by the library is ~5
+  if (size >= 10) {
+    for (size_t x = 0; x < size; x++) {
+      pass_through_command_play_pushed.push_back(data[x]);
+    }
+
+    auto test_packet =
+        TestPassThroughPacket::Make(pass_through_command_play_pushed);
+    test_packet->GetKeyState();
+    test_packet->GetOperationId();
+
+    test_packet =
+        TestPassThroughPacket::Make(pass_through_command_play_released);
+    test_packet->GetKeyState();
+    test_packet->GetOperationId();
+    test_packet->GetData();
+    test_packet->IsValid();
+    test_packet->ToString();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/play_item_packet_fuzzer.cc b/packet/tests/fuzzers/play_item_packet_fuzzer.cc
new file mode 100644
index 0000000..90057b3
--- /dev/null
+++ b/packet/tests/fuzzers/play_item_packet_fuzzer.cc
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+// Adapted from play_item_packet_test.cc
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+#include "play_item.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestPlayItemReqPacket = TestPacketType<PlayItemRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> play_item_request;
+
+  // We will use stability testing to see the sensible max size for fuzzing.
+  // Max BT packet size = 251
+  // Expected packet size by the library is ~21 but previous tests account for
+  // as low as 4
+  if (size >= 21) {
+    for (size_t x = 0; x < size; x++) {
+      play_item_request.push_back(data[x]);
+    }
+
+    auto test_packet = TestPlayItemReqPacket::Make(play_item_request);
+
+    test_packet->GetScope();
+    test_packet->GetUid();
+    test_packet->GetUidCounter();
+    test_packet->IsValid();
+    test_packet->GetData();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/register_notification_packet_fuzzer.cc b/packet/tests/fuzzers/register_notification_packet_fuzzer.cc
new file mode 100644
index 0000000..70328d0
--- /dev/null
+++ b/packet/tests/fuzzers/register_notification_packet_fuzzer.cc
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+// Adapted from register_notification_packet_test.cc
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+#include "register_notification_packet.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestRegNotifReqPacket = TestPacketType<RegisterNotificationRequest>;
+using TestRegNotifRspPacket = TestPacketType<RegisterNotificationResponse>;
+// as small as 4
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> register_play_status_notification;
+
+  // We will use stability testing to see the sensible max size for fuzzing.
+  // Max BT packet size = 251
+  // Expected packet size by the library is as small as 4.
+  // Seems to raise exceptions below 15.
+
+  if (size >= 15) {
+    for (size_t x = 0; x < size; x++) {
+      register_play_status_notification.push_back(data[x]);
+    }
+
+    auto test_packet =
+        TestRegNotifReqPacket::Make(register_play_status_notification);
+
+    test_packet->GetEventRegistered();
+    test_packet->GetInterval();
+    test_packet->GetData();
+    test_packet->ToString();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/reject_packet_fuzzer.cc b/packet/tests/fuzzers/reject_packet_fuzzer.cc
new file mode 100644
index 0000000..8ef7747
--- /dev/null
+++ b/packet/tests/fuzzers/reject_packet_fuzzer.cc
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+// Adapted from avrcp_reject_fuzzer.cc
+
+#include <gtest/gtest.h>
+
+#include "avrcp_reject_packet.h"
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestAvrcpPacket = TestPacketType<Packet>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  // Minimum size is 13.
+  std::vector<uint8_t> rejected_volume_changed_notification;
+
+  if (size > 13) {
+    for (size_t x = 0; x < size; x++) {
+      rejected_volume_changed_notification.push_back(data[x]);
+    }
+
+    auto test_packet =
+        TestAvrcpPacket::Make(rejected_volume_changed_notification);
+
+    test_packet->GetData();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/set_absolute_volume_packet_fuzzer.cc b/packet/tests/fuzzers/set_absolute_volume_packet_fuzzer.cc
new file mode 100644
index 0000000..28713a9
--- /dev/null
+++ b/packet/tests/fuzzers/set_absolute_volume_packet_fuzzer.cc
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+// Adapted from set_absolute_volume_packet_test.cc
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+#include "set_absolute_volume.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestSetVolumeRspPacket = TestPacketType<SetAbsoluteVolumeResponse>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> set_absolute_volume_response;
+
+  // We will use stability testing to see the sensible max size for fuzzing.
+  // Max BT packet size = 251
+  // Expected packet size by the library is ~5
+  if (size >= 12) {
+    for (size_t x = 0; x < size; x++) {
+      set_absolute_volume_response.push_back(data[x]);
+    }
+    auto test_packet =
+        TestSetVolumeRspPacket::Make(set_absolute_volume_response);
+
+    test_packet->IsValid();
+    test_packet->GetVolume();
+    test_packet->GetData();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/set_addressed_player_packet_fuzzer.cc b/packet/tests/fuzzers/set_addressed_player_packet_fuzzer.cc
new file mode 100644
index 0000000..83277b6
--- /dev/null
+++ b/packet/tests/fuzzers/set_addressed_player_packet_fuzzer.cc
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+// set_addressed_player_packet_test.cc
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+#include "set_addressed_player.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestSetAddrPlayerPacket = TestPacketType<SetAddressedPlayerRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> short_set_addressed_player_request;
+  // Expected packet size by the library is ~9
+  if (size >= 9) {
+    for (size_t x = 0; x < size; x++) {
+      short_set_addressed_player_request.push_back(data[x]);
+    }
+    auto test_packet =
+        TestSetAddrPlayerPacket::Make(set_addressed_player_request);
+
+    test_packet->GetPlayerId();
+    test_packet->GetData();
+  }
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/set_browsed_player_req_fuzzer.cc b/packet/tests/fuzzers/set_browsed_player_req_fuzzer.cc
new file mode 100644
index 0000000..0586d3f
--- /dev/null
+++ b/packet/tests/fuzzers/set_browsed_player_req_fuzzer.cc
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+// Adapted from set_browsed_player_packet.cc
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+#include "set_browsed_player.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestSetBrowsedPlayerPacket = TestPacketType<SetBrowsedPlayerRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> set_browsed_player_response;
+
+  // Expected packet size by the library is ~5
+  if (size >= 5) {
+    for (size_t x = 0; x < size; x++) {
+      set_browsed_player_response.push_back(data[x]);
+    }
+
+    auto test_packet =
+        TestSetBrowsedPlayerPacket::Make(set_browsed_player_response);
+
+    test_packet->GetPlayerId();
+    test_packet->GetData();
+    test_packet->IsValid();
+    test_packet->ToString();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/set_browsed_player_res_fuzzer.cc b/packet/tests/fuzzers/set_browsed_player_res_fuzzer.cc
new file mode 100644
index 0000000..e26e2c5
--- /dev/null
+++ b/packet/tests/fuzzers/set_browsed_player_res_fuzzer.cc
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+#include "set_browsed_player.h"
+
+namespace bluetooth {
+namespace avrcp {
+
+using TestSetBrowsedPlayerPacket = TestPacketType<SetBrowsedPlayerRequest>;
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> set_browsed_player_request;
+
+  // Expected packet size by the library is ~5
+  if (size >= 5) {
+    for (size_t x = 0; x < size; x++) {
+      set_browsed_player_request.push_back(data[x]);
+    }
+
+    auto test_packet =
+        TestSetBrowsedPlayerPacket::Make(set_browsed_player_request);
+
+    test_packet->GetPlayerId();
+    test_packet->GetData();
+    test_packet->IsValid();
+  }
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/packet/tests/fuzzers/vendor_packet_fuzzer.cc b/packet/tests/fuzzers/vendor_packet_fuzzer.cc
new file mode 100644
index 0000000..50a8c5d
--- /dev/null
+++ b/packet/tests/fuzzers/vendor_packet_fuzzer.cc
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+// Adapted from vendor_packet_test.cc
+
+#include <gtest/gtest.h>
+#include <tuple>
+
+#include "avrcp_test_packets.h"
+#include "packet_test_helper.h"
+#include "vendor_packet.h"
+
+namespace bluetooth {
+
+namespace avrcp {
+
+using TestVendorPacket = TestPacketType<VendorPacket>;
+
+using TestParam = std::tuple<std::vector<uint8_t>, CommandPdu>;
+class VendorPacketTest : public ::testing::TestWithParam<TestParam> {
+ public:
+  std::vector<uint8_t> GetPacketData() { return std::get<0>(GetParam()); }
+  CommandPdu GetCommandPdu() { return std::get<1>(GetParam()); }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  std::vector<uint8_t> short_vendor_packet;
+
+  // Expected packet size by the library is > 19
+  if (size >= 19) {
+    for (size_t x = 0; x < size; x++) {
+      short_vendor_packet.push_back(data[x]);
+    }
+
+  } else {
+    return 0;
+  }
+
+  auto test_packet = TestVendorPacket::Make(short_vendor_packet);
+
+  test_packet->GetCompanyId();
+  test_packet->GetCommandPdu();
+  test_packet->GetPacketType();
+  test_packet->GetParameterLength();
+  test_packet->IsValid();
+  test_packet->ToString();
+
+  return 0;
+}
+
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/profile/avrcp/Android.bp b/profile/avrcp/Android.bp
index 515f513..3705c64 100644
--- a/profile/avrcp/Android.bp
+++ b/profile/avrcp/Android.bp
@@ -50,6 +50,7 @@
         "libosi",
         "liblog",
         "libcutils",
+        "libbase",
         "libbtdevice",
         "avrcp-target-service",
     ],
diff --git a/profile/avrcp/connection_handler.cc b/profile/avrcp/connection_handler.cc
index 7b2765a..af8fb57 100644
--- a/profile/avrcp/connection_handler.cc
+++ b/profile/avrcp/connection_handler.cc
@@ -132,7 +132,7 @@
     return;
   };
 
-  return SdpLookup(bdaddr, base::Bind(connection_lambda, this, bdaddr));
+  return SdpLookup(bdaddr, base::Bind(connection_lambda, this, bdaddr), false);
 }
 
 bool ConnectionHandler::DisconnectDevice(const RawAddress& bdaddr) {
@@ -155,7 +155,8 @@
   return list;
 }
 
-bool ConnectionHandler::SdpLookup(const RawAddress& bdaddr, SdpCallback cb) {
+bool ConnectionHandler::SdpLookup(const RawAddress& bdaddr, SdpCallback cb,
+                                  bool retry) {
   LOG(INFO) << __PRETTY_FUNCTION__;
 
   tAVRC_SDP_DB_PARAMS db_params;
@@ -172,11 +173,11 @@
   db_params.p_db = disc_db;
   db_params.p_attrs = attr_list;
 
-  return avrc_->FindService(
-             UUID_SERVCLASS_AV_REMOTE_CONTROL, bdaddr, &db_params,
-             base::Bind(&ConnectionHandler::SdpCb,
-                        weak_ptr_factory_.GetWeakPtr(), bdaddr, cb, disc_db)) ==
-         AVRC_SUCCESS;
+  return avrc_->FindService(UUID_SERVCLASS_AV_REMOTE_CONTROL, bdaddr,
+                            &db_params,
+                            base::Bind(&ConnectionHandler::SdpCb,
+                                       weak_ptr_factory_.GetWeakPtr(), bdaddr,
+                                       cb, disc_db, retry)) == AVRC_SUCCESS;
 }
 
 bool ConnectionHandler::AvrcpConnect(bool initiator, const RawAddress& bdaddr) {
@@ -342,7 +343,7 @@
         }
       };
 
-      SdpLookup(*peer_addr, base::Bind(sdp_lambda, this, handle));
+      SdpLookup(*peer_addr, base::Bind(sdp_lambda, this, handle), false);
 
       avrc_->OpenBrowse(handle, AVCT_ACP);
       AvrcpConnect(false, RawAddress::kAny);
@@ -406,10 +407,15 @@
 }
 
 void ConnectionHandler::SdpCb(const RawAddress& bdaddr, SdpCallback cb,
-                              tSDP_DISCOVERY_DB* disc_db, uint16_t status) {
+                              tSDP_DISCOVERY_DB* disc_db, bool retry,
+                              uint16_t status) {
   LOG(INFO) << __PRETTY_FUNCTION__ << ": SDP lookup callback received";
 
-  if (status != AVRC_SUCCESS) {
+  if (status == SDP_CONN_FAILED and !retry) {
+    LOG(WARNING) << __PRETTY_FUNCTION__ << ": SDP Failure retry again";
+    SdpLookup(bdaddr, cb, true);
+    return;
+  } else if (status != AVRC_SUCCESS) {
     LOG(ERROR) << __PRETTY_FUNCTION__
                << ": SDP Failure: status = " << (unsigned int)status;
     cb.Run(status, 0, 0);
diff --git a/profile/avrcp/connection_handler.h b/profile/avrcp/connection_handler.h
index e22cb6a..d5f0a27 100644
--- a/profile/avrcp/connection_handler.h
+++ b/profile/avrcp/connection_handler.h
@@ -135,9 +135,9 @@
 
   using SdpCallback = base::Callback<void(uint16_t status, uint16_t version,
                                           uint16_t features)>;
-  virtual bool SdpLookup(const RawAddress& bdaddr, SdpCallback cb);
+  virtual bool SdpLookup(const RawAddress& bdaddr, SdpCallback cb, bool retry);
   void SdpCb(const RawAddress& bdaddr, SdpCallback cb,
-             tSDP_DISCOVERY_DB* disc_db, uint16_t status);
+             tSDP_DISCOVERY_DB* disc_db, bool retry, uint16_t status);
 
   virtual bool AvrcpConnect(bool initiator, const RawAddress& bdaddr);
 
diff --git a/profile/avrcp/device.cc b/profile/avrcp/device.cc
index 4ca624b..a7e0e9a 100644
--- a/profile/avrcp/device.cc
+++ b/profile/avrcp/device.cc
@@ -98,6 +98,19 @@
       case CommandPdu::REGISTER_NOTIFICATION: {
         auto register_notification =
             Packet::Specialize<RegisterNotificationResponse>(pkt);
+
+        if (!register_notification->IsValid()) {
+          DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
+          auto response =
+              RejectBuilder::MakeBuilder(pkt->GetCommandPdu(),
+                                         Status::INVALID_PARAMETER);
+          send_message(label, false, std::move(response));
+          active_labels_.erase(label);
+          volume_interface_ = nullptr;
+          volume_ = VOL_REGISTRATION_FAILED;
+          return;
+        }
+
         if (register_notification->GetEvent() != Event::VOLUME_CHANGED) {
           DEVICE_LOG(WARNING)
               << __func__ << ": Unhandled register notification received: "
@@ -336,16 +349,6 @@
     uint8_t label, const std::shared_ptr<RegisterNotificationResponse>& pkt) {
   DEVICE_VLOG(1) << __func__ << ": interim=" << pkt->IsInterim();
 
-  if (!pkt->IsValid()) {
-    DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
-    auto response = RejectBuilder::MakeBuilder(pkt->GetCommandPdu(), Status::INVALID_PARAMETER);
-    send_message(label, false, std::move(response));
-    active_labels_.erase(label);
-    volume_interface_ = nullptr;
-    volume_ = VOL_REGISTRATION_FAILED;
-    return;
-  }
-
   if (volume_interface_ == nullptr) return;
 
   if (pkt->GetCType() == CType::REJECTED) {
@@ -992,9 +995,16 @@
   DEVICE_VLOG(2) << __func__ << ": media_id=\"" << media_id << "\"";
 
   SongInfo info;
-  for (const auto& temp : song_list) {
-    if (temp.media_id == media_id) {
-      info = temp;
+  if (song_list.size() == 1) {
+    DEVICE_VLOG(2)
+        << __func__
+        << " Send out the only song in the queue as now playing song.";
+    info = song_list.front();
+  } else {
+    for (const auto& temp : song_list) {
+      if (temp.media_id == media_id) {
+        info = temp;
+      }
     }
   }
 
diff --git a/proto/Android.bp b/proto/Android.bp
deleted file mode 100644
index c8a852d..0000000
--- a/proto/Android.bp
+++ /dev/null
@@ -1,18 +0,0 @@
-java_library_static {
-    name: "bluetooth-protos-lite",
-    host_supported: true,
-    proto: {
-        type: "lite",
-    },
-    srcs: ["bluetooth/metrics/bluetooth.proto"],
-}
-
-cc_library_static {
-    name: "libbt-protos-lite",
-    host_supported: true,
-    proto: {
-        export_proto_headers: true,
-        type: "lite",
-    },
-    srcs: ["bluetooth/metrics/bluetooth.proto"],
-}
diff --git a/service/Android.bp b/service/Android.bp
index 4b21d0d..2760b5b 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -63,6 +63,7 @@
     "hal/fake_bluetooth_gatt_interface.cc",
     "hal/fake_bluetooth_interface.cc",
     "test/a2dp_sink_unittest.cc",
+    "test/a2dp_source_unittest.cc",
     "test/adapter_unittest.cc",
     "test/advertise_data_unittest.cc",
     "test/fake_hal_util.cc",
diff --git a/service/a2dp_source.cc b/service/a2dp_source.cc
index 9e77007..d39f202 100644
--- a/service/a2dp_source.cc
+++ b/service/a2dp_source.cc
@@ -199,6 +199,13 @@
                              codecs_selectable_capabilities);
 }
 
+bool A2dpSource::MandatoryCodecPreferredCallback(BluetoothAvInterface* iface,
+                                                 const RawAddress& bd_addr) {
+  LockGuard lock(delegate_mutex_);
+  // Do nothing. Optional codecs are preferred by default.
+  return false;
+}
+
 // A2dpSourceFactory implementation
 // ========================================================
 A2dpSourceFactory::A2dpSourceFactory() = default;
diff --git a/service/a2dp_source.h b/service/a2dp_source.h
index f4e3ab3..53da6bd 100644
--- a/service/a2dp_source.h
+++ b/service/a2dp_source.h
@@ -82,6 +82,8 @@
       const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
       const std::vector<btav_a2dp_codec_config_t>
           codecs_selectable_capabilities) override;
+  bool MandatoryCodecPreferredCallback(hal::BluetoothAvInterface* iface,
+                                       const RawAddress& bd_addr) override;
 
   // For |GetAppIdentifier|.
   const Uuid app_identifier_;
diff --git a/service/gatt_server.cc b/service/gatt_server.cc
index 08c4f83..5754780 100644
--- a/service/gatt_server.cc
+++ b/service/gatt_server.cc
@@ -18,6 +18,7 @@
 
 #include <base/logging.h>
 
+#include "osi/include/log.h"
 #include "service/logging_helpers.h"
 #include "stack/include/bt_types.h"
 
@@ -118,6 +119,12 @@
     return false;
   }
 
+  if (offset < 0) {
+    android_errorWriteLog(0x534e4554, "143231677");
+    LOG(ERROR) << "Offset is less than 0 offset: " << offset;
+    return false;
+  }
+
   if (value.size() + offset > BTGATT_MAX_ATTR_LEN) {
     LOG(ERROR) << "Value is too large";
     return false;
diff --git a/service/gatt_server_old.cc b/service/gatt_server_old.cc
index cfc394c..95cd6cc 100644
--- a/service/gatt_server_old.cc
+++ b/service/gatt_server_old.cc
@@ -131,7 +131,7 @@
 /** Callback invoked in response to register_server */
 void RegisterServerCallback(int status, int server_if,
                             const bluetooth::Uuid& app_uuid) {
-  LOG_INFO(LOG_TAG, "%s: status:%d server_if:%d app_uuid:%p", __func__, status,
+  LOG_INFO("%s: status:%d server_if:%d app_uuid:%p", __func__, status,
            server_if, &app_uuid);
 
   g_internal->server_if = server_if;
@@ -144,9 +144,8 @@
 
 void ServiceAddedCallback(int status, int server_if,
                           std::vector<btgatt_db_element_t> service) {
-  LOG_INFO(LOG_TAG, "%s: status:%d server_if:%d count:%zu svc_handle:%d",
-           __func__, status, server_if, service.size(),
-           service[0].attribute_handle);
+  LOG_INFO("%s: status:%d server_if:%d count:%zu svc_handle:%d", __func__,
+           status, server_if, service.size(), service[0].attribute_handle);
 
   std::lock_guard<std::mutex> lock(g_internal->lock);
   g_internal->server_if = server_if;
@@ -158,12 +157,12 @@
   for (size_t i = 1; i < service.size(); i++) {
     const btgatt_db_element_t& el = service[i];
     if (el.type == BTGATT_DB_DESCRIPTOR) {
-      LOG_INFO(LOG_TAG, "%s: descr_handle:%d", __func__, el.attribute_handle);
+      LOG_INFO("%s: descr_handle:%d", __func__, el.attribute_handle);
     } else if (el.type == BTGATT_DB_CHARACTERISTIC) {
       bluetooth::Uuid id(el.uuid);
       uint16_t char_handle = el.attribute_handle;
 
-      LOG_INFO(LOG_TAG, "%s: char_handle:%d", __func__, char_handle);
+      LOG_INFO("%s: char_handle:%d", __func__, char_handle);
 
       g_internal->uuid_to_attribute[id] = char_handle;
       g_internal->characteristics[char_handle].uuid = id;
@@ -202,7 +201,7 @@
 
   bt_status_t btstat = g_internal->gatt->client->register_client(client_id);
   if (btstat != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: Failed to register client", __func__);
+    LOG_ERROR("%s: Failed to register client", __func__);
   }
 }
 
@@ -227,11 +226,11 @@
   const size_t attribute_size = std::min(kMaxGattAttributeSize, blob_remaining);
 
   std::string addr(BtAddrString(&bda));
-  LOG_INFO(LOG_TAG,
-           "%s: connection:%d (%s) reading attr:%d attribute_offset_octets:%d "
-           "blob_section:%u (is_long:%u)",
-           __func__, conn_id, addr.c_str(), attr_handle,
-           attribute_offset_octets, ch.blob_section, is_long);
+  LOG_INFO(
+      "%s: connection:%d (%s) reading attr:%d attribute_offset_octets:%d "
+      "blob_section:%u (is_long:%u)",
+      __func__, conn_id, addr.c_str(), attr_handle, attribute_offset_octets,
+      ch.blob_section, is_long);
 
   btgatt_response_t response;
   response.attr_value.len = 0;
@@ -253,12 +252,12 @@
                           int attr_handle, int attribute_offset, bool need_rsp,
                           bool is_prep, std::vector<uint8_t> value) {
   std::string addr(BtAddrString(&bda));
-  LOG_INFO(LOG_TAG,
-           "%s: connection:%d (%s:trans:%d) write attr:%d attribute_offset:%d "
-           "length:%zu "
-           "need_resp:%u is_prep:%u",
-           __func__, conn_id, addr.c_str(), trans_id, attr_handle,
-           attribute_offset, value.size(), need_rsp, is_prep);
+  LOG_INFO(
+      "%s: connection:%d (%s:trans:%d) write attr:%d attribute_offset:%d "
+      "length:%zu "
+      "need_resp:%u is_prep:%u",
+      __func__, conn_id, addr.c_str(), trans_id, attr_handle, attribute_offset,
+      value.size(), need_rsp, is_prep);
 
   std::lock_guard<std::mutex> lock(g_internal->lock);
 
@@ -274,7 +273,7 @@
   if (target_blob != g_internal->controlled_blobs.end() &&
       ch.blob.size() == 1u) {
     g_internal->characteristics[target_blob->second].blob_section = ch.blob[0];
-    LOG_INFO(LOG_TAG, "%s: updating attribute %d blob_section to %u", __func__,
+    LOG_INFO("%s: updating attribute %d blob_section to %u", __func__,
              target_blob->second, ch.blob[0]);
   } else if (!is_prep) {
     // This is a single frame characteristic write.
@@ -284,7 +283,7 @@
     OSI_NO_INTR(status = write(g_internal->pipefd[kPipeWriteEnd],
                                attr_uuid.data(), attr_uuid.size()));
     if (-1 == status)
-      LOG_ERROR(LOG_TAG, "%s: write failed: %s", __func__, strerror(errno));
+      LOG_ERROR("%s: write failed: %s", __func__, strerror(errno));
   } else {
     // This is a multi-frame characteristic write.
     // Wait for an 'RequestExecWriteCallback' to notify completion.
@@ -308,8 +307,8 @@
 void RequestExecWriteCallback(int conn_id, int trans_id, const RawAddress& bda,
                               int exec_write) {
   std::string addr(BtAddrString(&bda));
-  LOG_INFO(LOG_TAG, "%s: connection:%d (%s:trans:%d) exec_write:%d", __func__,
-           conn_id, addr.c_str(), trans_id, exec_write);
+  LOG_INFO("%s: connection:%d (%s:trans:%d) exec_write:%d", __func__, conn_id,
+           addr.c_str(), trans_id, exec_write);
 
   // This 'response' data is unused for ExecWriteResponses.
   // It is only used to pass BlueDroid argument validation.
@@ -325,14 +324,14 @@
   OSI_NO_INTR(status = write(g_internal->pipefd[kPipeWriteEnd], uuid.data(),
                              uuid.size()));
   if (-1 == status)
-    LOG_ERROR(LOG_TAG, "%s: write failed: %s", __func__, strerror(errno));
+    LOG_ERROR("%s: write failed: %s", __func__, strerror(errno));
 }
 
 void ConnectionCallback(int conn_id, int server_if, int connected,
                         const RawAddress& bda) {
   std::string addr(BtAddrString(&bda));
-  LOG_INFO(LOG_TAG, "%s: connection:%d server_if:%d connected:%d addr:%s",
-           __func__, conn_id, server_if, connected, addr.c_str());
+  LOG_INFO("%s: connection:%d server_if:%d connected:%d addr:%s", __func__,
+           conn_id, server_if, connected, addr.c_str());
   if (connected == 1) {
     g_internal->connections.insert(conn_id);
   } else if (connected == 0) {
@@ -341,7 +340,7 @@
 }
 
 void EnableAdvertisingCallback(uint8_t status) {
-  LOG_INFO(LOG_TAG, "%s: status:%d", __func__, status);
+  LOG_INFO("%s: status:%d", __func__, status);
   // This terminates a Start call.
   std::lock_guard<std::mutex> lock(g_internal->lock);
   g_internal->api_synchronize.notify_one();
@@ -349,8 +348,8 @@
 
 void RegisterClientCallback(int status, int client_if,
                             const bluetooth::Uuid& app_uuid) {
-  LOG_INFO(LOG_TAG, "%s: status:%d client_if:%d uuid[0]:%s", __func__, status,
-           client_if, app_uuid.ToString().c_str());
+  LOG_INFO("%s: status:%d client_if:%d uuid[0]:%s", __func__, status, client_if,
+           app_uuid.ToString().c_str());
   g_internal->client_if = client_if;
 
   // Setup our advertisement. This has no callback.
@@ -364,8 +363,8 @@
 }
 
 void ServiceStoppedCallback(int status, int server_if, int srvc_handle) {
-  LOG_INFO(LOG_TAG, "%s: status:%d server_if:%d srvc_handle:%d", __func__,
-           status, server_if, srvc_handle);
+  LOG_INFO("%s: status:%d server_if:%d srvc_handle:%d", __func__, status,
+           server_if, srvc_handle);
   // This terminates a Stop call.
   // TODO(icoolidge): make this symmetric with start
   std::lock_guard<std::mutex> lock(g_internal->lock);
@@ -386,15 +385,15 @@
 void ClientConnectCallback(int conn_id, int status, int client_if,
                            const RawAddress& bda) {
   std::string addr(BtAddrString(&bda));
-  LOG_INFO(LOG_TAG, "%s: conn_id:%d status:%d client_if:%d %s", __func__,
-           conn_id, status, client_if, addr.c_str());
+  LOG_INFO("%s: conn_id:%d status:%d client_if:%d %s", __func__, conn_id,
+           status, client_if, addr.c_str());
 }
 
 void ClientDisconnectCallback(int conn_id, int status, int client_if,
                               const RawAddress& bda) {
   std::string addr(BtAddrString(&bda));
-  LOG_INFO(LOG_TAG, "%s: conn_id:%d status:%d client_if:%d %s", __func__,
-           conn_id, status, client_if, addr.c_str());
+  LOG_INFO("%s: conn_id:%d status:%d client_if:%d %s", __func__, conn_id,
+           status, client_if, addr.c_str());
 }
 
 void IndicationSentCallback(UNUSED_ATTR int conn_id, UNUSED_ATTR int status) {
@@ -482,19 +481,19 @@
   gatt = reinterpret_cast<const btgatt_interface_t*>(
       bt_iface->get_profile_interface(BT_PROFILE_GATT_ID));
   if (!gatt) {
-    LOG_ERROR(LOG_TAG, "Error getting GATT interface");
+    LOG_ERROR("Error getting GATT interface");
     return -1;
   }
 
   bt_status_t btstat = gatt->init(&gatt_callbacks);
   if (btstat != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "Failed to initialize gatt interface");
+    LOG_ERROR("Failed to initialize gatt interface");
     return -1;
   }
 
   int status = pipe(pipefd);
   if (status == -1) {
-    LOG_ERROR(LOG_TAG, "pipe creation failed: %s", strerror(errno));
+    LOG_ERROR("pipe creation failed: %s", strerror(errno));
     return -1;
   }
 
@@ -534,7 +533,7 @@
 bool Server::Initialize(const Uuid& service_id, int* gatt_pipe) {
   internal_.reset(new ServerInternals);
   if (!internal_) {
-    LOG_ERROR(LOG_TAG, "Error creating internals");
+    LOG_ERROR("Error creating internals");
     return false;
   }
   g_internal = internal_.get();
@@ -542,25 +541,25 @@
   std::unique_lock<std::mutex> lock(internal_->lock);
   int status = internal_->Initialize();
   if (status) {
-    LOG_ERROR(LOG_TAG, "Error initializing internals");
+    LOG_ERROR("Error initializing internals");
     return false;
   }
 
   bt_status_t btstat = internal_->gatt->server->register_server(service_id);
   if (btstat != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "Failed to register server");
+    LOG_ERROR("Failed to register server");
     return false;
   }
 
   internal_->api_synchronize.wait(lock);
   // TODO(icoolidge): Better error handling.
   if (internal_->server_if == 0) {
-    LOG_ERROR(LOG_TAG, "Initialization of server failed");
+    LOG_ERROR("Initialization of server failed");
     return false;
   }
 
   *gatt_pipe = internal_->pipefd[kPipeReadEnd];
-  LOG_INFO(LOG_TAG, "Server Initialize succeeded");
+  LOG_INFO("Server Initialize succeeded");
   return true;
 }
 
@@ -624,7 +623,7 @@
   bt_status_t btstat =
       internal_->AddCharacteristic(id, properties, permissions);
   if (btstat != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "Failed to add characteristic to service: 0x%04x",
+    LOG_ERROR("Failed to add characteristic to service: 0x%04x",
               internal_->service_handle);
     return false;
   }
@@ -642,7 +641,7 @@
   bt_status_t btstat =
       internal_->AddCharacteristic(id, properties, permissions);
   if (btstat != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "Failed to set scan response data");
+    LOG_ERROR("Failed to set scan response data");
     return false;
   }
 
@@ -662,7 +661,7 @@
   bt_status_t btstat = internal_->gatt->server->add_service(
       internal_->server_if, pending_svc_decl);
   if (btstat != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "Failed to start service with handle: 0x%04x",
+    LOG_ERROR("Failed to start service with handle: 0x%04x",
               internal_->service_handle);
     return false;
   }
@@ -675,7 +674,7 @@
   bt_status_t btstat = internal_->gatt->server->stop_service(
       internal_->server_if, internal_->service_handle);
   if (btstat != BT_STATUS_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "Failed to stop service with handle: 0x%04x",
+    LOG_ERROR("Failed to stop service with handle: 0x%04x",
               internal_->service_handle);
     return false;
   }
diff --git a/service/hal/bluetooth_av_interface.cc b/service/hal/bluetooth_av_interface.cc
index 68f5bd0..4700cce 100644
--- a/service/hal/bluetooth_av_interface.cc
+++ b/service/hal/bluetooth_av_interface.cc
@@ -46,11 +46,11 @@
 base::ObserverList<BluetoothAvInterface::A2dpSinkObserver>*
 GetA2dpSinkObservers();
 
-#define VERIFY_INTERFACE_OR_RETURN()                                   \
+#define VERIFY_INTERFACE_OR_RETURN(...)                                \
   do {                                                                 \
     if (!g_interface) {                                                \
       LOG(WARNING) << "Callback received while |g_interface| is NULL"; \
-      return;                                                          \
+      return __VA_ARGS__;                                              \
     }                                                                  \
   } while (0)
 
@@ -88,6 +88,17 @@
   }
 }
 
+bool SourceMandatoryCodecPreferredCallback(const RawAddress& bd_addr) {
+  VERIFY_INTERFACE_OR_RETURN(false);
+  // The mandatory codec is preferred only when all observers disable their
+  // optional codecs.
+  for (auto& observer : *GetA2dpSourceObservers()) {
+    if (!observer.MandatoryCodecPreferredCallback(g_interface, bd_addr))
+      return false;
+  }
+  return true;
+}
+
 void SinkConnectionStateCallback(const RawAddress& bd_addr,
                                  btav_connection_state_t state) {
   std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
@@ -121,6 +132,7 @@
     .connection_state_cb = SourceConnectionStateCallback,
     .audio_state_cb = SourceAudioStateCallback,
     .audio_config_cb = SourceAudioConfigCallback,
+    .mandatory_codec_preferred_cb = SourceMandatoryCodecPreferredCallback,
 };
 
 btav_sink_callbacks_t av_sink_callbacks = {
@@ -146,9 +158,11 @@
 
     // Right now we only support one connected audio device.
     int max_connected_audio_devices = 1;
+    std::vector<btav_a2dp_codec_config_t> offloading_preference(0);
     if (hal_source_iface_->init(
             &av_source_callbacks, max_connected_audio_devices,
-            std::move(codec_priorities)) != BT_STATUS_SUCCESS) {
+            std::move(codec_priorities),
+            std::move(offloading_preference)) != BT_STATUS_SUCCESS) {
       LOG(ERROR) << "Failed to initialize HAL A2DP source interface";
       return false;
     }
@@ -295,6 +309,12 @@
   // Do nothing.
 }
 
+bool BluetoothAvInterface::A2dpSourceObserver::MandatoryCodecPreferredCallback(
+    BluetoothAvInterface* iface, const RawAddress& bd_addr) {
+  // Do nothing.
+  return false;
+}
+
 void BluetoothAvInterface::A2dpSinkObserver::ConnectionStateCallback(
     BluetoothAvInterface* iface, const RawAddress& bd_addr,
     btav_connection_state_t state) {
diff --git a/service/hal/bluetooth_av_interface.h b/service/hal/bluetooth_av_interface.h
index 9346143..eda6476 100644
--- a/service/hal/bluetooth_av_interface.h
+++ b/service/hal/bluetooth_av_interface.h
@@ -41,6 +41,8 @@
         const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
         const std::vector<btav_a2dp_codec_config_t>
             codecs_selectable_capabilities);
+    virtual bool MandatoryCodecPreferredCallback(BluetoothAvInterface* iface,
+                                                 const RawAddress& bd_addr);
 
    protected:
     virtual ~A2dpSourceObserver() = default;
diff --git a/service/hal/bluetooth_interface.cc b/service/hal/bluetooth_interface.cc
index 5ca220c..cc5492c 100644
--- a/service/hal/bluetooth_interface.cc
+++ b/service/hal/bluetooth_interface.cc
@@ -254,7 +254,7 @@
 
     // Initialize the Bluetooth interface. Set up the adapter (Bluetooth DM) API
     // callbacks.
-    status = hal_iface_->init(&bt_callbacks, false, false);
+    status = hal_iface_->init(&bt_callbacks, false, false, 0, nullptr);
     if (status != BT_STATUS_SUCCESS) {
       LOG(ERROR) << "Failed to initialize Bluetooth stack";
       return false;
diff --git a/service/hal/fake_bluetooth_av_interface.cc b/service/hal/fake_bluetooth_av_interface.cc
index 39a6331..01c8ebe 100644
--- a/service/hal/fake_bluetooth_av_interface.cc
+++ b/service/hal/fake_bluetooth_av_interface.cc
@@ -23,19 +23,30 @@
 // The global test handler instances. We have to have globals since the HAL
 // interface methods all have to be global and their signatures don't allow us
 // to pass in user_data.
+std::shared_ptr<FakeBluetoothAvInterface::TestA2dpSourceHandler>
+    g_a2dp_source_handler{};
 std::shared_ptr<FakeBluetoothAvInterface::TestA2dpSinkHandler>
-    g_a2dp_sink_handler;
+    g_a2dp_sink_handler{};
 
-bt_status_t FakeInit(btav_sink_callbacks_t* callbacks) {
+bt_status_t FakeSourceInit(
+    btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
+    const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+    const std::vector<btav_a2dp_codec_config_t>& offloading_preference) {
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t FakeSinkInit(btav_sink_callbacks_t* callbacks) {
   return BT_STATUS_SUCCESS;
 }
 
 bt_status_t FakeConnect(const RawAddress& bd_addr) {
+  if (g_a2dp_source_handler) return g_a2dp_source_handler->Connect(bd_addr);
   if (g_a2dp_sink_handler) return g_a2dp_sink_handler->Connect(bd_addr);
   return BT_STATUS_FAIL;
 }
 
 bt_status_t FakeDisconnect(const RawAddress& bd_addr) {
+  if (g_a2dp_source_handler) return g_a2dp_source_handler->Disconnect(bd_addr);
   if (g_a2dp_sink_handler) return g_a2dp_sink_handler->Disconnect(bd_addr);
   return BT_STATUS_FAIL;
 }
@@ -53,26 +64,36 @@
 
 btav_source_interface_t fake_a2dp_source_interface = {
     .size = sizeof(btav_source_interface_t),
-    .init = nullptr,
-    .connect = nullptr,
-    .disconnect = nullptr,
+    .init = FakeSourceInit,
+    .connect = FakeConnect,
+    .disconnect = FakeDisconnect,
+    .set_silence_device = nullptr,
+    .set_active_device = nullptr,
     .config_codec = nullptr,
-    .cleanup = nullptr,
+    .cleanup = FakeCleanup,
 };
 
 btav_sink_interface_t fake_a2dp_sink_interface = {
     .size = sizeof(btav_sink_interface_t),
-    .init = FakeInit,
+    .init = FakeSinkInit,
     .connect = FakeConnect,
     .disconnect = FakeDisconnect,
     .cleanup = FakeCleanup,
     .set_audio_focus_state = FakeSetAudioFocusState,
     .set_audio_track_gain = FakeSetAudioTrackGain,
+    .set_active_device = nullptr,
 };
 
 }  // namespace
 
 FakeBluetoothAvInterface::FakeBluetoothAvInterface(
+    std::shared_ptr<TestA2dpSourceHandler> a2dp_source_handler) {
+  CHECK(!g_a2dp_source_handler);
+
+  if (a2dp_source_handler) g_a2dp_source_handler = a2dp_source_handler;
+}
+
+FakeBluetoothAvInterface::FakeBluetoothAvInterface(
     std::shared_ptr<TestA2dpSinkHandler> a2dp_sink_handler) {
   CHECK(!g_a2dp_sink_handler);
 
@@ -80,21 +101,50 @@
 }
 
 FakeBluetoothAvInterface::~FakeBluetoothAvInterface() {
-  g_a2dp_sink_handler = nullptr;
+  g_a2dp_source_handler = {};
+  g_a2dp_sink_handler = {};
 }
 
 void FakeBluetoothAvInterface::NotifyConnectionState(
     const RawAddress& bda, btav_connection_state_t state) {
+  for (auto& observer : a2dp_source_observers_) {
+    observer.ConnectionStateCallback(this, bda, state);
+  }
   for (auto& observer : a2dp_sink_observers_) {
     observer.ConnectionStateCallback(this, bda, state);
   }
 }
 void FakeBluetoothAvInterface::NotifyAudioState(const RawAddress& bda,
                                                 btav_audio_state_t state) {
+  for (auto& observer : a2dp_source_observers_) {
+    observer.AudioStateCallback(this, bda, state);
+  }
   for (auto& observer : a2dp_sink_observers_) {
     observer.AudioStateCallback(this, bda, state);
   }
 }
+void FakeBluetoothAvInterface::NotifyAudioConfig(
+    const RawAddress& bda, const btav_a2dp_codec_config_t& codec_config,
+    const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
+    const std::vector<btav_a2dp_codec_config_t>
+        codecs_selectable_capabilities) {
+  for (auto& observer : a2dp_source_observers_) {
+    observer.AudioConfigCallback(this, bda, codec_config,
+                                 codecs_local_capabilities,
+                                 codecs_selectable_capabilities);
+  }
+}
+bool FakeBluetoothAvInterface::QueryMandatoryCodecPreferred(
+    const RawAddress& bda) {
+  // The mandatory codec is preferred only when all observers disable their
+  // optional codecs.
+  for (auto& observer : a2dp_source_observers_) {
+    if (!observer.MandatoryCodecPreferredCallback(this, bda)) {
+      return false;
+    }
+  }
+  return true;
+}
 void FakeBluetoothAvInterface::NotifyAudioConfig(const RawAddress& bda,
                                                  uint32_t sample_rate,
                                                  uint8_t channel_count) {
diff --git a/service/hal/fake_bluetooth_av_interface.h b/service/hal/fake_bluetooth_av_interface.h
index c979434..34aeac8 100644
--- a/service/hal/fake_bluetooth_av_interface.h
+++ b/service/hal/fake_bluetooth_av_interface.h
@@ -26,9 +26,16 @@
 
 class FakeBluetoothAvInterface : public BluetoothAvInterface {
  public:
-  // Handles HAL Bluetooth A2DP sink API calls for testing. Test code can
-  // provide a fake or mock implementation of this and all calls will be routed
-  // to it.
+  // Handles HAL Bluetooth A2DP API calls for testing. Test code can provide a
+  // fake or mock implementation of this and all calls will be routed to it.
+  class TestA2dpSourceHandler {
+   public:
+    virtual bt_status_t Connect(RawAddress bda) = 0;
+    virtual bt_status_t Disconnect(RawAddress bda) = 0;
+
+   protected:
+    virtual ~TestA2dpSourceHandler() = default;
+  };
   class TestA2dpSinkHandler {
    public:
     virtual bt_status_t Connect(RawAddress bda) = 0;
@@ -44,16 +51,26 @@
   // provide their own handlers or simply pass "nullptr" for the default
   // behavior in which BT_STATUS_FAIL will be returned from all calls.
   FakeBluetoothAvInterface(
+      std::shared_ptr<TestA2dpSourceHandler> a2dp_source_handler);
+  FakeBluetoothAvInterface(
       std::shared_ptr<TestA2dpSinkHandler> a2dp_sink_handler);
   ~FakeBluetoothAvInterface();
 
   // The methods below can be used to notify observers with certain events and
   // given parameters.
 
-  // A2DP sink callbacks
+  // A2DP common callbacks
   void NotifyConnectionState(const RawAddress& bda,
                              btav_connection_state_t state);
   void NotifyAudioState(const RawAddress& bda, btav_audio_state_t state);
+  // A2DP source callbacks
+  void NotifyAudioConfig(
+      const RawAddress& bda, const btav_a2dp_codec_config_t& codec_config,
+      const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
+      const std::vector<btav_a2dp_codec_config_t>
+          codecs_selectable_capabilities);
+  bool QueryMandatoryCodecPreferred(const RawAddress& bda);
+  // A2DP sink callbacks
   void NotifyAudioConfig(const RawAddress& bda, uint32_t sample_rate,
                          uint8_t channel_count);
 
@@ -73,7 +90,6 @@
  private:
   base::ObserverList<A2dpSourceObserver> a2dp_source_observers_;
   base::ObserverList<A2dpSinkObserver> a2dp_sink_observers_;
-  std::shared_ptr<TestA2dpSinkHandler> scanner_handler_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeBluetoothAvInterface);
 };
diff --git a/service/hal/fake_bluetooth_interface.cc b/service/hal/fake_bluetooth_interface.cc
index f1d03ad..4af8bc6 100644
--- a/service/hal/fake_bluetooth_interface.cc
+++ b/service/hal/fake_bluetooth_interface.cc
@@ -75,6 +75,7 @@
     nullptr, /* interop_database_add */
     nullptr, /* get_avrcp_service */
     nullptr, /* obfuscate_address */
+    nullptr, /* get_metric_id */
 };
 
 }  // namespace
diff --git a/service/ipc/linux_ipc_host.cc b/service/ipc/linux_ipc_host.cc
index 705748f..ebd41f0 100644
--- a/service/ipc/linux_ipc_host.cc
+++ b/service/ipc/linux_ipc_host.cc
@@ -82,7 +82,7 @@
     int status =
         TEMP_FAILURE_RETRY(ppoll(pfds_.data(), pfds_.size(), nullptr, nullptr));
     if (status < 1) {
-      LOG_ERROR(LOG_TAG, "ppoll error");
+      LOG_ERROR("ppoll error");
       return false;
     }
 
@@ -111,7 +111,7 @@
   bool status = gatt_servers_[service_uuid]->Initialize(
       Uuid::FromString(service_uuid), &gattfd);
   if (!status) {
-    LOG_ERROR(LOG_TAG, "Failed to initialize bluetooth");
+    LOG_ERROR("Failed to initialize bluetooth");
     return false;
   }
   pfds_.resize(kPossibleFds);
@@ -181,9 +181,8 @@
                                       const std::string& advertise_data,
                                       const std::string& manufacturer_data,
                                       const std::string& transmit_name) {
-  LOG_INFO(LOG_TAG, "%s: service:%s uuids:%s data:%s", __func__,
-           service_uuid.c_str(), advertise_uuids.c_str(),
-           advertise_data.c_str());
+  LOG_INFO("%s: service:%s uuids:%s data:%s", __func__, service_uuid.c_str(),
+           advertise_uuids.c_str(), advertise_data.c_str());
 
   std::vector<std::string> advertise_uuid_tokens = base::SplitString(
       advertise_uuids, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
@@ -251,20 +250,20 @@
   OSI_NO_INTR(size =
                   recv(pfds_[kFdIpc].fd, &ipc_msg[0], 0, MSG_PEEK | MSG_TRUNC));
   if (-1 == size) {
-    LOG_ERROR(LOG_TAG, "Error reading datagram size: %s", strerror(errno));
+    LOG_ERROR("Error reading datagram size: %s", strerror(errno));
     return false;
   } else if (0 == size) {
-    LOG_INFO(LOG_TAG, "%s:%d: Connection closed", __func__, __LINE__);
+    LOG_INFO("%s:%d: Connection closed", __func__, __LINE__);
     return false;
   }
 
   ipc_msg.resize(size);
   OSI_NO_INTR(size = read(pfds_[kFdIpc].fd, &ipc_msg[0], ipc_msg.size()));
   if (-1 == size) {
-    LOG_ERROR(LOG_TAG, "Error reading IPC: %s", strerror(errno));
+    LOG_ERROR("Error reading IPC: %s", strerror(errno));
     return false;
   } else if (0 == size) {
-    LOG_INFO(LOG_TAG, "%s:%d: Connection closed", __func__, __LINE__);
+    LOG_INFO("%s:%d: Connection closed", __func__, __LINE__);
     return false;
   }
 
@@ -300,7 +299,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "Malformed IPC message: %s", ipc_msg.c_str());
+  LOG_ERROR("Malformed IPC message: %s", ipc_msg.c_str());
   return false;
 }
 
@@ -310,7 +309,7 @@
 
   OSI_NO_INTR(r = read(pfds_[kFdGatt].fd, id.data(), id.size()));
   if (r != id.size()) {
-    LOG_ERROR(LOG_TAG, "Error reading GATT attribute ID");
+    LOG_ERROR("Error reading GATT attribute ID");
     return false;
   }
 
@@ -329,7 +328,7 @@
 
   OSI_NO_INTR(r = write(pfds_[kFdIpc].fd, transmit.data(), transmit.size()));
   if (-1 == r) {
-    LOG_ERROR(LOG_TAG, "Error replying to IPC: %s", strerror(errno));
+    LOG_ERROR("Error replying to IPC: %s", strerror(errno));
     return false;
   }
 
diff --git a/service/test/a2dp_source_unittest.cc b/service/test/a2dp_source_unittest.cc
new file mode 100644
index 0000000..5fb1961
--- /dev/null
+++ b/service/test/a2dp_source_unittest.cc
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "service/a2dp_source.h"
+#include "service/hal/fake_bluetooth_av_interface.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace bluetooth {
+namespace {
+
+class MockA2dpSourceHandler
+    : public hal::FakeBluetoothAvInterface::TestA2dpSourceHandler {
+ public:
+  MockA2dpSourceHandler() = default;
+  ~MockA2dpSourceHandler() override = default;
+
+  MOCK_METHOD1(Connect, bt_status_t(RawAddress));
+  MOCK_METHOD1(Disconnect, bt_status_t(RawAddress));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockA2dpSourceHandler);
+};
+
+class TestDelegate : public A2dpSource::Delegate {
+ public:
+  TestDelegate() = default;
+  ~TestDelegate() override = default;
+
+  struct RequestData {
+    std::string device_address;
+    int state = -1;
+    int count = 0;
+  };
+
+  // A2dpSource::Delegate implementation:
+  void OnConnectionState(const std::string& device_address,
+                         int state) override {
+    ++connection_state_.count;
+    connection_state_.device_address = device_address;
+    connection_state_.state = state;
+  }
+  void OnAudioState(const std::string& device_address, int state) override {
+    ++audio_state_.count;
+    audio_state_.device_address = device_address;
+    audio_state_.state = state;
+  }
+  void OnAudioConfig(
+      const std::string& device_address, A2dpCodecConfig codec_config,
+      const std::vector<A2dpCodecConfig>& codecs_local_capabilities,
+      const std::vector<A2dpCodecConfig>& codecs_selectable_capabilities)
+      override {
+    ++audio_config_.count;
+    audio_config_.device_address = device_address;
+  }
+
+  const RequestData& connection_state() const { return connection_state_; }
+  const RequestData& audio_state() const { return audio_state_; }
+  const RequestData& audio_config() const { return audio_config_; }
+
+ private:
+  RequestData connection_state_;
+  RequestData audio_state_;
+  RequestData audio_config_;
+};
+
+class A2dpSourceTest : public ::testing::Test {
+ public:
+  A2dpSourceTest() = default;
+  ~A2dpSourceTest() override = default;
+
+  void SetUp() override {
+    mock_handler_.reset(new MockA2dpSourceHandler());
+    fake_hal_av_iface_ = new hal::FakeBluetoothAvInterface(mock_handler_);
+    hal::BluetoothAvInterface::InitializeForTesting(fake_hal_av_iface_);
+    factory_.reset(new A2dpSourceFactory());
+  }
+
+  void TearDown() override {
+    factory_.reset();
+    hal::BluetoothAvInterface::CleanUp();
+  }
+
+ protected:
+  hal::FakeBluetoothAvInterface* fake_hal_av_iface_;
+  std::shared_ptr<MockA2dpSourceHandler> mock_handler_;
+  std::unique_ptr<A2dpSourceFactory> factory_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(A2dpSourceTest);
+};
+
+class A2dpSourcePostRegisterTest : public A2dpSourceTest {
+ public:
+  A2dpSourcePostRegisterTest() = default;
+  ~A2dpSourcePostRegisterTest() override = default;
+
+  void SetUp() override {
+    A2dpSourceTest::SetUp();
+    Uuid uuid = Uuid::GetRandom();
+    auto callback = [&](BLEStatus status, const Uuid& in_uuid,
+                        std::unique_ptr<BluetoothInstance> in_client) {
+      CHECK(in_uuid == uuid);
+      CHECK(in_client.get());
+      CHECK(status == BLE_STATUS_SUCCESS);
+
+      a2dp_source_ = std::unique_ptr<A2dpSource>(
+          static_cast<A2dpSource*>(in_client.release()));
+    };
+
+    factory_->RegisterInstance(uuid, callback);
+  }
+
+  void TearDown() override {
+    a2dp_source_ = nullptr;
+    A2dpSourceTest::TearDown();
+  }
+
+ protected:
+  void Connect(const std::string& addr) {
+    RawAddress hal_addr;
+    ASSERT_TRUE(RawAddress::FromString(addr, hal_addr));
+
+    EXPECT_CALL(*mock_handler_, Connect(hal_addr))
+        .WillOnce(Return(BT_STATUS_SUCCESS));
+
+    EXPECT_TRUE(a2dp_source_->Connect(addr));
+  }
+
+  void Disconnect(const std::string& addr) {
+    RawAddress hal_addr;
+    ASSERT_TRUE(RawAddress::FromString(addr, hal_addr));
+
+    EXPECT_CALL(*mock_handler_, Disconnect(hal_addr))
+        .WillOnce(Return(BT_STATUS_SUCCESS));
+
+    EXPECT_TRUE(a2dp_source_->Disconnect(addr));
+  }
+
+  std::unique_ptr<A2dpSource> a2dp_source_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(A2dpSourcePostRegisterTest);
+};
+
+TEST_F(A2dpSourceTest, RegisterA2dpSource) {
+  // These will be asynchronously populate with a result when the callback
+  // executes.
+  BLEStatus status = BLE_STATUS_SUCCESS;
+  Uuid cb_uuid;
+  std::unique_ptr<A2dpSource> a2dp_source;
+  int callback_count = 0;
+
+  auto callback = [&](BLEStatus in_status, const Uuid& uuid,
+                      std::unique_ptr<BluetoothInstance> in_a2dp_source) {
+    status = in_status;
+    cb_uuid = uuid;
+    a2dp_source = std::unique_ptr<A2dpSource>(
+        static_cast<A2dpSource*>(in_a2dp_source.release()));
+    callback_count++;
+  };
+
+  Uuid uuid0 = Uuid::GetRandom();
+
+  // This should always succeed.
+  EXPECT_TRUE(factory_->RegisterInstance(uuid0, callback));
+  EXPECT_EQ(1, callback_count);
+
+  testing::Mock::VerifyAndClearExpectations(mock_handler_.get());
+
+  ASSERT_TRUE(a2dp_source.get() !=
+              nullptr);  // Assert to terminate in case of error
+  EXPECT_EQ(BLE_STATUS_SUCCESS, status);
+  EXPECT_EQ(bluetooth::A2dpSource::kSingletonInstanceId,
+            a2dp_source->GetInstanceId());
+  EXPECT_EQ(uuid0, a2dp_source->GetAppIdentifier());
+  EXPECT_EQ(uuid0, cb_uuid);
+
+  testing::Mock::VerifyAndClearExpectations(mock_handler_.get());
+}
+
+TEST_F(A2dpSourcePostRegisterTest, Connect) {
+  static const char kTestAddr[] = "AA:BB:CC:DD:EE:FF";
+  Connect(kTestAddr);
+  Disconnect(kTestAddr);
+}
+
+TEST_F(A2dpSourcePostRegisterTest, CallbackTest) {
+  static const char kTestAddr[] = "AA:BB:CC:DD:EE:FF";
+  RawAddress hal_addr;
+  ASSERT_TRUE(RawAddress::FromString(kTestAddr, hal_addr));
+
+  TestDelegate delegate;
+  a2dp_source_->SetDelegate(&delegate);
+  Connect(kTestAddr);
+
+  // OnConnectionState
+  const int kConnectionState = 2;
+  EXPECT_EQ(0, delegate.connection_state().count);
+  fake_hal_av_iface_->NotifyConnectionState(
+      hal_addr, static_cast<btav_connection_state_t>(kConnectionState));
+  EXPECT_EQ(1, delegate.connection_state().count);
+  EXPECT_EQ(kTestAddr, delegate.connection_state().device_address);
+  EXPECT_EQ(kConnectionState, delegate.connection_state().state);
+
+  // OnAudioState
+  const int kAudioState = 1;
+  EXPECT_EQ(0, delegate.audio_state().count);
+  fake_hal_av_iface_->NotifyAudioState(
+      hal_addr, static_cast<btav_audio_state_t>(kAudioState));
+  EXPECT_EQ(1, delegate.audio_state().count);
+  EXPECT_EQ(kTestAddr, delegate.audio_state().device_address);
+  EXPECT_EQ(kAudioState, delegate.audio_state().state);
+
+  // OnAudioConfig
+  const btav_a2dp_codec_config_t codec_config{};
+  const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities(0);
+  const std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities(0);
+  EXPECT_EQ(0, delegate.audio_config().count);
+  fake_hal_av_iface_->NotifyAudioConfig(hal_addr, codec_config,
+                                        codecs_local_capabilities,
+                                        codecs_selectable_capabilities);
+  EXPECT_EQ(1, delegate.audio_config().count);
+  EXPECT_EQ(kTestAddr, delegate.audio_config().device_address);
+
+  fake_hal_av_iface_->QueryMandatoryCodecPreferred(hal_addr);
+
+  Disconnect(kTestAddr);
+}
+
+}  // namespace
+}  // namespace bluetooth
diff --git a/stack/Android.bp b/stack/Android.bp
index 104f9f7f..c48aece 100644
--- a/stack/Android.bp
+++ b/stack/Android.bp
@@ -138,7 +138,6 @@
         "l2cap/l2c_link.cc",
         "l2cap/l2c_main.cc",
         "l2cap/l2c_utils.cc",
-        "l2cap/l2cap_client.cc",
         "pan/pan_api.cc",
         "pan/pan_main.cc",
         "pan/pan_utils.cc",
@@ -199,6 +198,7 @@
     ],
     srcs: [
         "test/stack_a2dp_test.cc",
+        "test/stack_avrcp_test.cc",
     ],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
@@ -235,9 +235,9 @@
         "libbt-utils",
         "libbtif",
         "libFraunhoferAAC",
-        "libg722codec",
-        "libbtdevice",
         "libbt-hci",
+        "libbtdevice",
+        "libg722codec",
         "libosi",
         "libudrv-uipc",
         "libbt-protos-lite",
@@ -467,3 +467,36 @@
         cfi: false,
     },
 }
+
+cc_test {
+    name: "net_test_stack_a2dp_native",
+    defaults: ["fluoride_defaults"],
+    test_suites: ["device-tests"],
+    host_supported: true,
+    include_dirs: [
+        "external/libldac/inc",
+        "system/bt",
+        "system/bt/stack/include",
+    ],
+    srcs: [
+        "test/a2dp/a2dp_vendor_ldac_decoder_test.cc",
+        "test/a2dp/misc_fake.cc",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "libcutils",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "libbt-common",
+        "libbt-protos-lite",
+        "liblog",
+        "libosi",
+        "libosi-AllocationTestHarness",
+    ],
+    sanitize: {
+        address: true,
+        cfi: true,
+        misc_undefined: ["bounds"],
+    },
+}
diff --git a/stack/BUILD.gn b/stack/BUILD.gn
index 00680c8..8b39594 100644
--- a/stack/BUILD.gn
+++ b/stack/BUILD.gn
@@ -122,7 +122,6 @@
     "l2cap/l2c_link.cc",
     "l2cap/l2c_main.cc",
     "l2cap/l2c_utils.cc",
-    "l2cap/l2cap_client.cc",
     "pan/pan_api.cc",
     "pan/pan_main.cc",
     "pan/pan_utils.cc",
@@ -202,6 +201,7 @@
   testonly = true
   sources = [
     "test/stack_a2dp_test.cc",
+    "test/stack_avrcp_test.cc",
   ]
 
   include_dirs = [
diff --git a/stack/a2dp/a2dp_aac.cc b/stack/a2dp/a2dp_aac.cc
index df641be..e58d7bd 100644
--- a/stack/a2dp/a2dp_aac.cc
+++ b/stack/a2dp/a2dp_aac.cc
@@ -35,6 +35,7 @@
 #include "bt_utils.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
+#include "osi/include/properties.h"
 
 #define A2DP_AAC_DEFAULT_BITRATE 320000  // 320 kbps
 #define A2DP_AAC_MIN_BITRATE 64000       // 64 kbps
@@ -50,8 +51,11 @@
   btav_a2dp_codec_bits_per_sample_t bits_per_sample;
 } tA2DP_AAC_CIE;
 
+static bool aac_source_caps_configured = false;
+static tA2DP_AAC_CIE a2dp_aac_source_caps = {};
+
 /* AAC Source codec capabilities */
-static const tA2DP_AAC_CIE a2dp_aac_source_caps = {
+static const tA2DP_AAC_CIE a2dp_aac_cbr_source_caps = {
     // objectType
     A2DP_AAC_OBJECT_TYPE_MPEG2_LC,
     // sampleRate
@@ -66,6 +70,22 @@
     // bits_per_sample
     BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16};
 
+/* AAC Source codec capabilities */
+static const tA2DP_AAC_CIE a2dp_aac_vbr_source_caps = {
+    // objectType
+    A2DP_AAC_OBJECT_TYPE_MPEG2_LC,
+    // sampleRate
+    // TODO: AAC 48.0kHz sampling rate should be added back - see b/62301376
+    A2DP_AAC_SAMPLING_FREQ_44100,
+    // channelMode
+    A2DP_AAC_CHANNEL_MODE_STEREO,
+    // variableBitRateSupport
+    A2DP_AAC_VARIABLE_BIT_RATE_ENABLED,
+    // bitRate
+    A2DP_AAC_DEFAULT_BITRATE,
+    // bits_per_sample
+    BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16};
+
 /* AAC Sink codec capabilities */
 static const tA2DP_AAC_CIE a2dp_aac_sink_caps = {
     // objectType
@@ -102,8 +122,12 @@
 };
 
 static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_aac = {
-    a2dp_aac_decoder_init, a2dp_aac_decoder_cleanup,
+    a2dp_aac_decoder_init,
+    a2dp_aac_decoder_cleanup,
     a2dp_aac_decoder_decode_packet,
+    nullptr,  // decoder_start
+    nullptr,  // decoder_suspend
+    nullptr,  // decoder_configure
 };
 
 UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAac(
@@ -271,23 +295,23 @@
   /* parse configuration */
   status = A2DP_ParseInfoAac(&cfg_cie, p_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: parsing failed %d", __func__, status);
+    LOG_ERROR("%s: parsing failed %d", __func__, status);
     return status;
   }
 
   /* verify that each parameter is in range */
 
-  LOG_VERBOSE(LOG_TAG, "%s: Object Type peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: Object Type peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.objectType, p_cap->objectType);
-  LOG_VERBOSE(LOG_TAG, "%s: Sample Rate peer: %u, capability %u", __func__,
+  LOG_VERBOSE("%s: Sample Rate peer: %u, capability %u", __func__,
               cfg_cie.sampleRate, p_cap->sampleRate);
-  LOG_VERBOSE(LOG_TAG, "%s: Channel Mode peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: Channel Mode peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.channelMode, p_cap->channelMode);
-  LOG_VERBOSE(
-      LOG_TAG, "%s: Variable Bit Rate Support peer: 0x%x, capability 0x%x",
-      __func__, cfg_cie.variableBitRateSupport, p_cap->variableBitRateSupport);
-  LOG_VERBOSE(LOG_TAG, "%s: Bit Rate peer: %u, capability %u", __func__,
-              cfg_cie.bitRate, p_cap->bitRate);
+  LOG_VERBOSE("%s: Variable Bit Rate Support peer: 0x%x, capability 0x%x",
+              __func__, cfg_cie.variableBitRateSupport,
+              p_cap->variableBitRateSupport);
+  LOG_VERBOSE("%s: Bit Rate peer: %u, capability %u", __func__, cfg_cie.bitRate,
+              p_cap->bitRate);
 
   /* Object Type */
   if ((cfg_cie.objectType & p_cap->objectType) == 0) return A2DP_BAD_OBJ_TYPE;
@@ -319,14 +343,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoAac(&aac_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoAac(&aac_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -342,14 +364,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoAac(&aac_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoAac(&aac_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -367,8 +387,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -408,8 +427,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -423,8 +441,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -444,8 +461,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -465,8 +481,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -489,8 +504,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -511,8 +525,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -533,8 +546,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -547,8 +559,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAac(&aac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -704,7 +715,19 @@
 
 const char* A2DP_CodecIndexStrAacSink(void) { return "AAC SINK"; }
 
+void aac_source_caps_initialize() {
+  if (aac_source_caps_configured) {
+    return;
+  }
+  a2dp_aac_source_caps =
+      osi_property_get_bool("persist.bluetooth.a2dp_aac.vbr_supported", false)
+          ? a2dp_aac_vbr_source_caps
+          : a2dp_aac_cbr_source_caps;
+  aac_source_caps_configured = true;
+}
+
 bool A2DP_InitCodecConfigAac(AvdtpSepConfig* p_cfg) {
+  aac_source_caps_initialize();
   if (A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aac_source_caps,
                         p_cfg->codec_info) != A2DP_SUCCESS) {
     return false;
@@ -750,6 +773,7 @@
     btav_a2dp_codec_priority_t codec_priority)
     : A2dpCodecConfigAacBase(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC,
                              A2DP_CodecIndexStrAac(), codec_priority, true) {
+  aac_source_caps_initialize();
   // Compute the local capability
   if (a2dp_aac_source_caps.sampleRate & A2DP_AAC_SAMPLING_FREQ_44100) {
     codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
@@ -780,7 +804,7 @@
 
   // Load the encoder
   if (!A2DP_LoadEncoderAac()) {
-    LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
+    LOG_ERROR("%s: cannot load the encoder", __func__);
     return false;
   }
 
@@ -1016,8 +1040,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoAac(&peer_info_cie, p_peer_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
 
@@ -1032,6 +1056,14 @@
   result_config_cie.variableBitRateSupport =
       p_a2dp_aac_caps->variableBitRateSupport &
       peer_info_cie.variableBitRateSupport;
+  if (result_config_cie.variableBitRateSupport !=
+      A2DP_AAC_VARIABLE_BIT_RATE_DISABLED) {
+    codec_config_.codec_specific_1 =
+        static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5);
+  } else {
+    codec_config_.codec_specific_1 =
+        static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_CBR);
+  }
 
   // Set the bit rate as follows:
   // 1. If the remote device reports a bogus bit rate
@@ -1146,10 +1178,10 @@
     }
   } while (false);
   if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match sample frequency: source caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, p_a2dp_aac_caps->sampleRate, peer_info_cie.sampleRate);
+    LOG_ERROR(
+        "%s: cannot match sample frequency: source caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, p_a2dp_aac_caps->sampleRate, peer_info_cie.sampleRate);
     goto fail;
   }
 
@@ -1221,11 +1253,11 @@
     }
   } while (false);
   if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match bits per sample: default = 0x%x "
-              "user preference = 0x%x",
-              __func__, a2dp_aac_default_config.bits_per_sample,
-              codec_user_config_.bits_per_sample);
+    LOG_ERROR(
+        "%s: cannot match bits per sample: default = 0x%x "
+        "user preference = 0x%x",
+        __func__, a2dp_aac_default_config.bits_per_sample,
+        codec_user_config_.bits_per_sample);
     goto fail;
   }
 
@@ -1296,24 +1328,48 @@
     }
   } while (false);
   if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match channel mode: source caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, p_a2dp_aac_caps->channelMode,
-              peer_info_cie.channelMode);
-    goto fail;
-  }
-
-  if (A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
-                        p_result_codec_config) != A2DP_SUCCESS) {
+    LOG_ERROR(
+        "%s: cannot match channel mode: source caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, p_a2dp_aac_caps->channelMode, peer_info_cie.channelMode);
     goto fail;
   }
 
   //
   // Copy the codec-specific fields if they are not zero
   //
-  if (codec_user_config_.codec_specific_1 != 0)
-    codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+  if (codec_user_config_.codec_specific_1 != 0) {
+    if (result_config_cie.variableBitRateSupport !=
+        A2DP_AAC_VARIABLE_BIT_RATE_DISABLED) {
+      auto user_bitrate_mode = codec_user_config_.codec_specific_1;
+      switch (static_cast<AacEncoderBitrateMode>(user_bitrate_mode)) {
+        case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_C:
+          // VBR is supported, and is disabled by the user preference
+          result_config_cie.variableBitRateSupport =
+              A2DP_AAC_VARIABLE_BIT_RATE_DISABLED;
+          [[fallthrough]];
+        case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_1:
+          [[fallthrough]];
+        case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_2:
+          [[fallthrough]];
+        case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_3:
+          [[fallthrough]];
+        case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_4:
+          [[fallthrough]];
+        case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5:
+          codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+          break;
+        default:
+          codec_config_.codec_specific_1 =
+              static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5);
+      }
+    } else {
+      // It is no needed to check the user preference when Variable Bitrate
+      // unsupported by one of source or sink
+      codec_config_.codec_specific_1 =
+          static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_CBR);
+    }
+  }
   if (codec_user_config_.codec_specific_2 != 0)
     codec_config_.codec_specific_2 = codec_user_config_.codec_specific_2;
   if (codec_user_config_.codec_specific_3 != 0)
@@ -1321,6 +1377,11 @@
   if (codec_user_config_.codec_specific_4 != 0)
     codec_config_.codec_specific_4 = codec_user_config_.codec_specific_4;
 
+  if (A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+                        p_result_codec_config) != A2DP_SUCCESS) {
+    goto fail;
+  }
+
   // Create a local copy of the peer codec capability/config, and the
   // result codec config.
   if (is_capability) {
@@ -1357,6 +1418,7 @@
   tA2DP_AAC_CIE peer_info_cie;
   uint8_t channelMode;
   uint16_t sampleRate;
+  uint8_t variableBitRateSupport;
   const tA2DP_AAC_CIE* p_a2dp_aac_caps =
       (is_source_) ? &a2dp_aac_source_caps : &a2dp_aac_sink_caps;
 
@@ -1370,8 +1432,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoAac(&peer_info_cie, p_peer_codec_capabilities, true);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
 
@@ -1409,6 +1471,17 @@
         BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
   }
 
+  // Compute the selectable capability - variable bitrate mode
+  variableBitRateSupport = p_a2dp_aac_caps->variableBitRateSupport &
+                           peer_info_cie.variableBitRateSupport;
+  if (variableBitRateSupport != A2DP_AAC_VARIABLE_BIT_RATE_DISABLED) {
+    codec_selectable_capability_.codec_specific_1 =
+        static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5);
+  } else {
+    codec_selectable_capability_.codec_specific_1 =
+        static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_CBR);
+  }
+
   status = A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie,
                              ota_codec_peer_capability_);
   CHECK(status == A2DP_SUCCESS);
@@ -1435,7 +1508,7 @@
 
   // Load the decoder
   if (!A2DP_LoadDecoderAac()) {
-    LOG_ERROR(LOG_TAG, "%s: cannot load the decoder", __func__);
+    LOG_ERROR("%s: cannot load the decoder", __func__);
     return false;
   }
 
diff --git a/stack/a2dp/a2dp_aac_decoder.cc b/stack/a2dp/a2dp_aac_decoder.cc
index d9cd85d..55c020d 100644
--- a/stack/a2dp/a2dp_aac_decoder.cc
+++ b/stack/a2dp/a2dp_aac_decoder.cc
@@ -30,7 +30,7 @@
 typedef struct {
   HANDLE_AACDECODER aac_handle;
   bool has_aac_handle;  // True if aac_handle is valid
-  INT_PCM* decode_buf;
+  INT_PCM* decode_buf = nullptr;
   decoded_data_callback_t decode_callback;
 } tA2DP_AAC_DECODER_CB;
 
@@ -58,7 +58,7 @@
 void a2dp_aac_decoder_cleanup(void) {
   if (a2dp_aac_decoder_cb.has_aac_handle)
     aacDecoder_Close(a2dp_aac_decoder_cb.aac_handle);
-  free(a2dp_aac_decoder_cb.decode_buf);
+  osi_free(a2dp_aac_decoder_cb.decode_buf);
   memset(&a2dp_aac_decoder_cb, 0, sizeof(a2dp_aac_decoder_cb));
 }
 
@@ -70,7 +70,7 @@
     AAC_DECODER_ERROR err = aacDecoder_Fill(a2dp_aac_decoder_cb.aac_handle,
                                             &pBuffer, &bufferSize, &bytesValid);
     if (err != AAC_DEC_OK) {
-      LOG_ERROR(LOG_TAG, "%s: aacDecoder_Fill failed: 0x%x", __func__,
+      LOG_ERROR("%s: aacDecoder_Fill failed: 0x%x", __func__,
                 static_cast<unsigned>(err));
       return false;
     }
@@ -83,7 +83,7 @@
         break;
       }
       if (err != AAC_DEC_OK) {
-        LOG_ERROR(LOG_TAG, "%s: aacDecoder_DecodeFrame failed: 0x%x", __func__,
+        LOG_ERROR("%s: aacDecoder_DecodeFrame failed: 0x%x", __func__,
                   static_cast<int>(err));
         break;
       }
@@ -91,7 +91,7 @@
       CStreamInfo* info =
           aacDecoder_GetStreamInfo(a2dp_aac_decoder_cb.aac_handle);
       if (!info || info->sampleRate <= 0) {
-        LOG_ERROR(LOG_TAG, "%s: Invalid stream info", __func__);
+        LOG_ERROR("%s: Invalid stream info", __func__);
         break;
       }
 
diff --git a/stack/a2dp/a2dp_aac_encoder.cc b/stack/a2dp/a2dp_aac_encoder.cc
index f6c68a6..5be7d92 100644
--- a/stack/a2dp/a2dp_aac_encoder.cc
+++ b/stack/a2dp/a2dp_aac_encoder.cc
@@ -19,6 +19,7 @@
 #include "a2dp_aac_encoder.h"
 
 #include <inttypes.h>
+#include <math.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -56,8 +57,8 @@
 
 typedef struct {
   uint32_t counter;
-  uint32_t bytes_per_tick; /* pcm bytes read each media task tick */
-  uint64_t last_frame_us;
+  uint32_t bytes_per_tick;              // pcm bytes read each media task tick
+  uint64_t last_frame_timestamp_100ns;  // values in 1/10 microseconds
 } tA2DP_AAC_FEEDING_STATE;
 
 typedef struct {
@@ -93,6 +94,8 @@
   a2dp_aac_encoder_stats_t stats;
 } tA2DP_AAC_ENCODER_CB;
 
+static uint32_t a2dp_aac_encoder_interval_ms = A2DP_AAC_ENCODER_INTERVAL_MS;
+
 static tA2DP_AAC_ENCODER_CB a2dp_aac_encoder_cb;
 
 static void a2dp_aac_encoder_update(uint16_t peer_mtu,
@@ -159,10 +162,10 @@
   a2dp_aac_encoder_cb.timestamp = 0;
 
   if (a2dp_aac_encoder_cb.peer_mtu == 0) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid peer MTU",
-              __func__, name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid peer MTU",
+        __func__, name().c_str());
     return false;
   }
 
@@ -193,18 +196,18 @@
     AACENC_ERROR aac_error = aacEncOpen(&a2dp_aac_encoder_cb.aac_handle, 0,
                                         2 /* max 2 channels: stereo */);
     if (aac_error != AACENC_OK) {
-      LOG_ERROR(LOG_TAG, "%s: Cannot open AAC encoder handle: AAC error 0x%x",
-                __func__, aac_error);
+      LOG_ERROR("%s: Cannot open AAC encoder handle: AAC error 0x%x", __func__,
+                aac_error);
       return;  // TODO: Return an error?
     }
     a2dp_aac_encoder_cb.has_aac_handle = true;
   }
 
   if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid codec config",
-              __func__, a2dp_codec_config->name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid codec config",
+        __func__, a2dp_codec_config->name().c_str());
     return;
   }
   const uint8_t* p_codec_info = codec_info;
@@ -215,29 +218,27 @@
   p_feeding_params->bits_per_sample =
       a2dp_codec_config->getAudioBitsPerSample();
   p_feeding_params->channel_count = A2DP_GetTrackChannelCountAac(p_codec_info);
-  LOG_DEBUG(LOG_TAG, "%s: sample_rate=%u bits_per_sample=%u channel_count=%u",
-            __func__, p_feeding_params->sample_rate,
-            p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
-  a2dp_aac_feeding_reset();
+  LOG_DEBUG("%s: sample_rate=%u bits_per_sample=%u channel_count=%u", __func__,
+            p_feeding_params->sample_rate, p_feeding_params->bits_per_sample,
+            p_feeding_params->channel_count);
 
   // The codec parameters
   p_encoder_params->sample_rate =
       a2dp_aac_encoder_cb.feeding_params.sample_rate;
   p_encoder_params->channel_mode = A2DP_GetChannelModeCodeAac(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: original AVDTP MTU size: %d", __func__,
+  LOG_VERBOSE("%s: original AVDTP MTU size: %d", __func__,
               a2dp_aac_encoder_cb.TxAaMtuSize);
   if (a2dp_aac_encoder_cb.is_peer_edr &&
       !a2dp_aac_encoder_cb.peer_supports_3mbps) {
     // This condition would be satisfied only if the remote device is
     // EDR and supports only 2 Mbps, but the effective AVDTP MTU size
     // exceeds the 2DH5 packet size.
-    LOG_VERBOSE(LOG_TAG,
-                "%s: The remote device is EDR but does not support 3 Mbps",
+    LOG_VERBOSE("%s: The remote device is EDR but does not support 3 Mbps",
                 __func__);
     if (peer_mtu > MAX_2MBPS_AVDTP_MTU) {
-      LOG_WARN(LOG_TAG, "%s: Restricting AVDTP MTU size from %d to %d",
-               __func__, peer_mtu, MAX_2MBPS_AVDTP_MTU);
+      LOG_WARN("%s: Restricting AVDTP MTU size from %d to %d", __func__,
+               peer_mtu, MAX_2MBPS_AVDTP_MTU);
       peer_mtu = MAX_2MBPS_AVDTP_MTU;
     }
   }
@@ -248,9 +249,9 @@
     a2dp_aac_encoder_cb.TxAaMtuSize = peer_mtu;
   }
 
-  LOG_DEBUG(LOG_TAG, "%s: MTU=%d, peer_mtu=%d", __func__,
+  LOG_DEBUG("%s: MTU=%d, peer_mtu=%d", __func__,
             a2dp_aac_encoder_cb.TxAaMtuSize, peer_mtu);
-  LOG_DEBUG(LOG_TAG, "%s: sample_rate: %d channel_mode: %d ", __func__,
+  LOG_DEBUG("%s: sample_rate: %d channel_mode: %d ", __func__,
             p_encoder_params->sample_rate, p_encoder_params->channel_mode);
 
   // Set the encoder's parameters: Audio Object Type - MANDATORY
@@ -274,19 +275,19 @@
       aac_param_value = AOT_AAC_SCAL;
       break;
     default:
-      LOG_ERROR(LOG_TAG,
-                "%s: Cannot set AAC parameter AACENC_AOT: "
-                "invalid object type %d",
-                __func__, object_type);
+      LOG_ERROR(
+          "%s: Cannot set AAC parameter AACENC_AOT: "
+          "invalid object type %d",
+          __func__, object_type);
       return;  // TODO: Return an error?
   }
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle, AACENC_AOT,
                                   aac_param_value);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_AOT to %d: "
-              "AAC error 0x%x",
-              __func__, aac_param_value, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_AOT to %d: "
+        "AAC error 0x%x",
+        __func__, aac_param_value, aac_error);
     return;  // TODO: Return an error?
   }
 
@@ -295,10 +296,10 @@
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
                                   AACENC_AUDIOMUXVER, aac_param_value);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_AUDIOMUXVER to %d: "
-              "AAC error 0x%x",
-              __func__, aac_param_value, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_AUDIOMUXVER to %d: "
+        "AAC error 0x%x",
+        __func__, aac_param_value, aac_error);
     return;  // TODO: Return an error?
   }
 
@@ -307,10 +308,10 @@
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
                                   AACENC_SIGNALING_MODE, aac_param_value);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_SIGNALING_MODE to %d: "
-              "AAC error 0x%x",
-              __func__, aac_param_value, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_SIGNALING_MODE to %d: "
+        "AAC error 0x%x",
+        __func__, aac_param_value, aac_error);
     return;  // TODO: Return an error?
   }
 
@@ -319,10 +320,10 @@
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
                                   AACENC_SAMPLERATE, aac_param_value);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_SAMPLERATE to %d: "
-              "AAC error 0x%x",
-              __func__, aac_param_value, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_SAMPLERATE to %d: "
+        "AAC error 0x%x",
+        __func__, aac_param_value, aac_error);
     return;  // TODO: Return an error?
   }
   aac_sampling_freq = aac_param_value;  // Save for extra usage below
@@ -333,23 +334,23 @@
   aac_peak_bit_rate =
       A2DP_ComputeMaxBitRateAac(p_codec_info, a2dp_aac_encoder_cb.TxAaMtuSize);
   aac_param_value = std::min(aac_param_value, aac_peak_bit_rate);
-  LOG_DEBUG(LOG_TAG, "%s: MTU = %d Sampling Frequency = %d Bit Rate = %d",
-            __func__, a2dp_aac_encoder_cb.TxAaMtuSize, aac_sampling_freq,
+  LOG_DEBUG("%s: MTU = %d Sampling Frequency = %d Bit Rate = %d", __func__,
+            a2dp_aac_encoder_cb.TxAaMtuSize, aac_sampling_freq,
             aac_param_value);
   if (aac_param_value == -1) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_BITRATE: "
-              "invalid codec bit rate",
-              __func__);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_BITRATE: "
+        "invalid codec bit rate",
+        __func__);
     return;  // TODO: Return an error?
   }
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
                                   AACENC_BITRATE, aac_param_value);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_BITRATE to %d: "
-              "AAC error 0x%x",
-              __func__, aac_param_value, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_BITRATE to %d: "
+        "AAC error 0x%x",
+        __func__, aac_param_value, aac_error);
     return;  // TODO: Return an error?
   }
 
@@ -357,10 +358,10 @@
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
                                   AACENC_PEAK_BITRATE, aac_peak_bit_rate);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_PEAK_BITRATE to %d: "
-              "AAC error 0x%x",
-              __func__, aac_peak_bit_rate, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_PEAK_BITRATE to %d: "
+        "AAC error 0x%x",
+        __func__, aac_peak_bit_rate, aac_error);
     return;  // TODO: Return an error?
   }
 
@@ -373,10 +374,10 @@
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
                                   AACENC_CHANNELMODE, aac_param_value);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_CHANNELMODE to %d: "
-              "AAC error 0x%x",
-              __func__, aac_param_value, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_CHANNELMODE to %d: "
+        "AAC error 0x%x",
+        __func__, aac_param_value, aac_error);
     return;  // TODO: Return an error?
   }
 
@@ -385,10 +386,10 @@
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
                                   AACENC_TRANSMUX, aac_param_value);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_TRANSMUX to %d: "
-              "AAC error 0x%x",
-              __func__, aac_param_value, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_TRANSMUX to %d: "
+        "AAC error 0x%x",
+        __func__, aac_param_value, aac_error);
     return;  // TODO: Return an error?
   }
 
@@ -397,29 +398,51 @@
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
                                   AACENC_HEADER_PERIOD, aac_param_value);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_HEADER_PERIOD to %d: "
-              "AAC error 0x%x",
-              __func__, aac_param_value, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_HEADER_PERIOD to %d: "
+        "AAC error 0x%x",
+        __func__, aac_param_value, aac_error);
     return;  // TODO: Return an error?
   }
 
   // Set the encoder's parameters: Variable Bit Rate Support
   aac_param_value = A2DP_GetVariableBitRateSupportAac(p_codec_info);
   if (aac_param_value == -1) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_BITRATEMODE: "
-              "invalid codec bit rate mode",
-              __func__);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_BITRATEMODE: "
+        "invalid codec bit rate mode",
+        __func__);
     return;  // TODO: Return an error?
+  } else if (aac_param_value == A2DP_AAC_VARIABLE_BIT_RATE_ENABLED) {
+    // VBR has 5 modes defined in external/aac/libAACenc/src/aacenc.h
+    // A2DP_AAC_VARIABLE_BIT_RATE_DISABLED is equal to AACENC_BR_MODE_CBR
+    auto bitrate_mode = a2dp_codec_config->getCodecConfig().codec_specific_1;
+    switch (static_cast<AacEncoderBitrateMode>(bitrate_mode)) {
+      case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_1:
+        [[fallthrough]];
+      case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_2:
+        [[fallthrough]];
+      case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_3:
+        [[fallthrough]];
+      case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_4:
+        [[fallthrough]];
+      case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5:
+        break;
+      default:
+        bitrate_mode =
+            static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5);
+    }
+    aac_param_value =
+        static_cast<uint8_t>(bitrate_mode) & ~A2DP_AAC_VARIABLE_BIT_RATE_MASK;
   }
+  LOG_INFO("%s: AACENC_BITRATEMODE: %d", __func__, aac_param_value);
   aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
                                   AACENC_BITRATEMODE, aac_param_value);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot set AAC parameter AACENC_BITRATEMODE to %d: "
-              "AAC error 0x%x",
-              __func__, aac_param_value, aac_error);
+    LOG_ERROR(
+        "%s: Cannot set AAC parameter AACENC_BITRATEMODE to %d: "
+        "AAC error 0x%x",
+        __func__, aac_param_value, aac_error);
     return;  // TODO: Return an error?
   }
 
@@ -427,8 +450,7 @@
   aac_error =
       aacEncEncode(a2dp_aac_encoder_cb.aac_handle, NULL, NULL, NULL, NULL);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot complete setting the AAC parameters: AAC error 0x%x",
+    LOG_ERROR("%s: Cannot complete setting the AAC parameters: AAC error 0x%x",
               __func__, aac_error);
     return;  // TODO: Return an error?
   }
@@ -437,20 +459,22 @@
   AACENC_InfoStruct aac_info;
   aac_error = aacEncInfo(a2dp_aac_encoder_cb.aac_handle, &aac_info);
   if (aac_error != AACENC_OK) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot retrieve the AAC encoder info: AAC error 0x%x",
+    LOG_ERROR("%s: Cannot retrieve the AAC encoder info: AAC error 0x%x",
               __func__, aac_error);
     return;  // TODO: Return an error?
   }
   p_encoder_params->frame_length = aac_info.frameLength;
   p_encoder_params->input_channels_n = aac_info.inputChannels;
   p_encoder_params->max_encoded_buffer_bytes = aac_info.maxOutBufBytes;
-  LOG_DEBUG(LOG_TAG,
-            "%s: AAC frame_length = %u input_channels_n = %u "
-            "max_encoded_buffer_bytes = %d",
-            __func__, p_encoder_params->frame_length,
-            p_encoder_params->input_channels_n,
-            p_encoder_params->max_encoded_buffer_bytes);
+  LOG_DEBUG(
+      "%s: AAC frame_length = %u input_channels_n = %u "
+      "max_encoded_buffer_bytes = %d",
+      __func__, p_encoder_params->frame_length,
+      p_encoder_params->input_channels_n,
+      p_encoder_params->max_encoded_buffer_bytes);
+
+  // After encoder params ready, reset the feeding state and its interval.
+  a2dp_aac_feeding_reset();
 }
 
 void a2dp_aac_encoder_cleanup(void) {
@@ -460,6 +484,23 @@
 }
 
 void a2dp_aac_feeding_reset(void) {
+  auto frame_length = a2dp_aac_encoder_cb.aac_encoder_params.frame_length;
+  auto sample_rate = a2dp_aac_encoder_cb.feeding_params.sample_rate;
+  if (frame_length == 0 || sample_rate == 0) {
+    LOG_WARN("%s: AAC encoder is not configured", __func__);
+    a2dp_aac_encoder_interval_ms = A2DP_AAC_ENCODER_INTERVAL_MS;
+  } else {
+    // PCM data size per AAC frame (bits)
+    // = aac_encoder_params.frame_length * feeding_params.bits_per_sample
+    //   * feeding_params.channel_count
+    // = feeding_params.sample_rate * feeding_params.bits_per_sample
+    //   * feeding_params.channel_count * (T_interval_ms / 1000);
+    // Here we use the nearest integer not greater than the value.
+    a2dp_aac_encoder_interval_ms = frame_length * 1000 / sample_rate;
+    if (a2dp_aac_encoder_interval_ms < A2DP_AAC_ENCODER_INTERVAL_MS)
+      a2dp_aac_encoder_interval_ms = A2DP_AAC_ENCODER_INTERVAL_MS;
+  }
+
   /* By default, just clear the entire state */
   memset(&a2dp_aac_encoder_cb.aac_feeding_state, 0,
          sizeof(a2dp_aac_encoder_cb.aac_feeding_state));
@@ -468,11 +509,12 @@
       (a2dp_aac_encoder_cb.feeding_params.sample_rate *
        a2dp_aac_encoder_cb.feeding_params.bits_per_sample / 8 *
        a2dp_aac_encoder_cb.feeding_params.channel_count *
-       A2DP_AAC_ENCODER_INTERVAL_MS) /
+       a2dp_aac_encoder_interval_ms) /
       1000;
 
-  LOG_DEBUG(LOG_TAG, "%s: PCM bytes per tick %u", __func__,
-            a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick);
+  LOG_INFO("%s: PCM bytes %u per tick %u ms", __func__,
+           a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick,
+           a2dp_aac_encoder_interval_ms);
 }
 
 void a2dp_aac_feeding_flush(void) {
@@ -480,7 +522,7 @@
 }
 
 uint64_t a2dp_aac_get_encoder_interval_ms(void) {
-  return A2DP_AAC_ENCODER_INTERVAL_MS;
+  return a2dp_aac_encoder_interval_ms;
 }
 
 void a2dp_aac_send_frames(uint64_t timestamp_us) {
@@ -488,8 +530,8 @@
   uint8_t nb_iterations = 0;
 
   a2dp_aac_get_num_frame_iteration(&nb_iterations, &nb_frame, timestamp_us);
-  LOG_VERBOSE(LOG_TAG, "%s: Sending %d frames per iteration, %d iterations",
-              __func__, nb_frame, nb_iterations);
+  LOG_VERBOSE("%s: Sending %d frames per iteration, %d iterations", __func__,
+              nb_frame, nb_iterations);
   if (nb_frame == 0) return;
 
   for (uint8_t counter = 0; counter < nb_iterations; counter++) {
@@ -512,26 +554,38 @@
       a2dp_aac_encoder_cb.aac_encoder_params.frame_length *
       a2dp_aac_encoder_cb.feeding_params.channel_count *
       a2dp_aac_encoder_cb.feeding_params.bits_per_sample / 8;
-  LOG_VERBOSE(LOG_TAG, "%s: pcm_bytes_per_frame %u", __func__,
-              pcm_bytes_per_frame);
+  LOG_VERBOSE("%s: pcm_bytes_per_frame %u", __func__, pcm_bytes_per_frame);
 
-  uint32_t us_this_tick = A2DP_AAC_ENCODER_INTERVAL_MS * 1000;
-  uint64_t now_us = timestamp_us;
-  if (a2dp_aac_encoder_cb.aac_feeding_state.last_frame_us != 0)
-    us_this_tick =
-        (now_us - a2dp_aac_encoder_cb.aac_feeding_state.last_frame_us);
-  a2dp_aac_encoder_cb.aac_feeding_state.last_frame_us = now_us;
+  uint32_t hecto_ns_this_tick = a2dp_aac_encoder_interval_ms * 10000;
+  uint64_t* last_100ns =
+      &a2dp_aac_encoder_cb.aac_feeding_state.last_frame_timestamp_100ns;
+  uint64_t now_100ns = timestamp_us * 10;
+  if (*last_100ns != 0) {
+    hecto_ns_this_tick = (now_100ns - *last_100ns);
+  }
+  *last_100ns = now_100ns;
 
-  a2dp_aac_encoder_cb.aac_feeding_state.counter +=
-      a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick * us_this_tick /
-      (A2DP_AAC_ENCODER_INTERVAL_MS * 1000);
+  uint32_t bytes_this_tick =
+      a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick *
+      hecto_ns_this_tick / (a2dp_aac_encoder_interval_ms * 10000);
+  a2dp_aac_encoder_cb.aac_feeding_state.counter += bytes_this_tick;
+  // Without this erratum, there was a three microseocnd shift per tick which
+  // would cause one frame mismatched after every 180 seconds
+  uint32_t erratum_100ns =
+      ceil(1.0f * bytes_this_tick * a2dp_aac_encoder_interval_ms * 10000 /
+           a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick);
+  if (erratum_100ns < hecto_ns_this_tick) {
+    LOG_VERBOSE("%s: hecto_ns_this_tick=%d, bytes=%d, erratum_100ns=%d",
+                __func__, hecto_ns_this_tick, bytes_this_tick, erratum_100ns);
+    *last_100ns -= hecto_ns_this_tick - erratum_100ns;
+  }
 
   result = a2dp_aac_encoder_cb.aac_feeding_state.counter / pcm_bytes_per_frame;
   a2dp_aac_encoder_cb.aac_feeding_state.counter -= result * pcm_bytes_per_frame;
   nof = result;
 
-  LOG_VERBOSE(LOG_TAG, "%s: effective num of frames %u, iterations %u",
-              __func__, nof, noi);
+  LOG_VERBOSE("%s: effective num of frames %u, iterations %u", __func__, nof,
+              noi);
 
   *num_of_frames = nof;
   *num_of_iterations = noi;
@@ -603,7 +657,7 @@
       if (a2dp_aac_read_feeding(read_buffer, &bytes_read)) {
         uint8_t* packet = (uint8_t*)(p_buf + 1) + p_buf->offset + p_buf->len;
         if (!a2dp_aac_encoder_cb.has_aac_handle) {
-          LOG_ERROR(LOG_TAG, "%s: invalid AAC handle", __func__);
+          LOG_ERROR("%s: invalid AAC handle", __func__);
           a2dp_aac_encoder_cb.stats.media_read_total_dropped_packets++;
           osi_free(p_buf);
           return;
@@ -614,8 +668,7 @@
             aacEncEncode(a2dp_aac_encoder_cb.aac_handle, &in_buf_desc,
                          &out_buf_desc, &aac_in_args, &aac_out_args);
         if (aac_error != AACENC_OK) {
-          LOG_ERROR(LOG_TAG, "%s: AAC encoding error: 0x%x", __func__,
-                    aac_error);
+          LOG_ERROR("%s: AAC encoding error: 0x%x", __func__, aac_error);
           a2dp_aac_encoder_cb.stats.media_read_total_dropped_packets++;
           osi_free(p_buf);
           return;
@@ -626,7 +679,7 @@
         nb_frame--;
         p_buf->layer_specific++;  // added a frame to the buffer
       } else {
-        LOG_WARN(LOG_TAG, "%s: underflow %d", __func__, nb_frame);
+        LOG_WARN("%s: underflow %d", __func__, nb_frame);
         a2dp_aac_encoder_cb.aac_feeding_state.counter +=
             nb_frame * p_encoder_params->frame_length *
             p_feeding_params->channel_count *
@@ -703,6 +756,14 @@
 
   A2dpCodecConfig::debug_codec_dump(fd);
 
+  auto codec_specific_1 = getCodecConfig().codec_specific_1;
+  dprintf(
+      fd,
+      "  AAC bitrate mode                                        : %s "
+      "(0x%" PRIx64 ")\n",
+      ((codec_specific_1 & ~A2DP_AAC_VARIABLE_BIT_RATE_MASK) == 0 ? "Constant"
+                                                                  : "Variable"),
+      codec_specific_1);
   dprintf(fd,
           "  Packet counts (expected/dropped)                        : %zu / "
           "%zu\n",
diff --git a/stack/a2dp/a2dp_api.cc b/stack/a2dp/a2dp_api.cc
index cc7d01e..8723735 100644
--- a/stack/a2dp/a2dp_api.cc
+++ b/stack/a2dp/a2dp_api.cc
@@ -70,7 +70,7 @@
   tSDP_PROTOCOL_ELEM elem;
   RawAddress peer_address = RawAddress::kEmpty;
 
-  LOG_INFO(LOG_TAG, "%s: status: %d", __func__, status);
+  LOG_INFO("%s: status: %d", __func__, status);
 
   if (status == SDP_SUCCESS) {
     /* loop through all records we found */
@@ -106,7 +106,7 @@
       /* get AVDTP version */
       if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_AVDTP, &elem)) {
         a2dp_svc.avdt_version = elem.params[0];
-        LOG_VERBOSE(LOG_TAG, "avdt_version: 0x%x", a2dp_svc.avdt_version);
+        LOG_VERBOSE("avdt_version: 0x%x", a2dp_svc.avdt_version);
       }
 
       /* we've got everything, we're done */
@@ -179,7 +179,7 @@
   uint8_t* p;
   tSDP_PROTOCOL_ELEM proto_list[A2DP_NUM_PROTO_ELEMS];
 
-  LOG_VERBOSE(LOG_TAG, "%s: uuid: 0x%x", __func__, service_uuid);
+  LOG_VERBOSE("%s: uuid: 0x%x", __func__, service_uuid);
 
   if ((sdp_handle == 0) || (service_uuid != UUID_SERVCLASS_AUDIO_SOURCE &&
                             service_uuid != UUID_SERVCLASS_AUDIO_SINK))
@@ -275,22 +275,22 @@
                               tA2DP_FIND_CBACK* p_cback) {
   bool result = true;
 
-  LOG_INFO(LOG_TAG, "%s: peer: %s UUID: 0x%x", __func__,
-           bd_addr.ToString().c_str(), service_uuid);
+  LOG_INFO("%s: peer: %s UUID: 0x%x", __func__, bd_addr.ToString().c_str(),
+           service_uuid);
   if ((service_uuid != UUID_SERVCLASS_AUDIO_SOURCE &&
        service_uuid != UUID_SERVCLASS_AUDIO_SINK) ||
       p_db == NULL || p_cback == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find service for peer %s UUID 0x%x: "
-              "invalid parameters",
-              __func__, bd_addr.ToString().c_str(), service_uuid);
+    LOG_ERROR(
+        "%s: cannot find service for peer %s UUID 0x%x: "
+        "invalid parameters",
+        __func__, bd_addr.ToString().c_str(), service_uuid);
     return A2DP_INVALID_PARAMS;
   }
 
   if (a2dp_cb.find.service_uuid == UUID_SERVCLASS_AUDIO_SOURCE ||
       a2dp_cb.find.service_uuid == UUID_SERVCLASS_AUDIO_SINK) {
-    LOG_ERROR(LOG_TAG, "%s: cannot find service for peer %s UUID 0x%x: busy",
-              __func__, bd_addr.ToString().c_str(), service_uuid);
+    LOG_ERROR("%s: cannot find service for peer %s UUID 0x%x: busy", __func__,
+              bd_addr.ToString().c_str(), service_uuid);
     return A2DP_BUSY;
   }
 
@@ -319,10 +319,10 @@
     }
   }
   if (!result) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find service for peer %s UUID 0x%x: "
-              "SDP error",
-              __func__, bd_addr.ToString().c_str(), service_uuid);
+    LOG_ERROR(
+        "%s: cannot find service for peer %s UUID 0x%x: "
+        "SDP error",
+        __func__, bd_addr.ToString().c_str(), service_uuid);
     return A2DP_FAIL;
   }
 
diff --git a/stack/a2dp/a2dp_codec_config.cc b/stack/a2dp/a2dp_codec_config.cc
index edf7e0c..f133374 100644
--- a/stack/a2dp/a2dp_codec_config.cc
+++ b/stack/a2dp/a2dp_codec_config.cc
@@ -106,7 +106,7 @@
 A2dpCodecConfig* A2dpCodecConfig::createCodec(
     btav_a2dp_codec_index_t codec_index,
     btav_a2dp_codec_priority_t codec_priority) {
-  LOG_DEBUG(LOG_TAG, "%s: codec %s", __func__, A2DP_CodecIndexStr(codec_index));
+  LOG_DEBUG("%s: codec %s", __func__, A2DP_CodecIndexStr(codec_index));
 
   A2dpCodecConfig* codec_config = nullptr;
   switch (codec_index) {
@@ -153,7 +153,7 @@
   memcpy(p_codec_info, ota_codec_config_, sizeof(ota_codec_config_));
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -166,7 +166,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return -1;
 }
 
@@ -235,7 +235,7 @@
         }
         p_a2dp_offload->codec_info[7] =
             codec_config[10];  // LDAC specific channel mode
-        LOG_VERBOSE(LOG_TAG, "%s: Ldac specific channelmode =%d", __func__,
+        LOG_VERBOSE("%s: Ldac specific channelmode =%d", __func__,
                     p_a2dp_offload->codec_info[7]);
       }
       break;
@@ -555,7 +555,7 @@
 }
 
 bool A2dpCodecs::init() {
-  LOG_DEBUG(LOG_TAG, "%s", __func__);
+  LOG_DEBUG("%s", __func__);
   std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
   char* tok = NULL;
   char* tmp_token = NULL;
@@ -574,19 +574,19 @@
     tok = strtok_r((char*)value_cap, "-", &tmp_token);
     while (tok != NULL) {
       if (strcmp(tok, "sbc") == 0) {
-        LOG_INFO(LOG_TAG, "%s: SBC offload supported", __func__);
+        LOG_INFO("%s: SBC offload supported", __func__);
         offload_codec_support[BTAV_A2DP_CODEC_INDEX_SOURCE_SBC] = true;
       } else if (strcmp(tok, "aac") == 0) {
-        LOG_INFO(LOG_TAG, "%s: AAC offload supported", __func__);
+        LOG_INFO("%s: AAC offload supported", __func__);
         offload_codec_support[BTAV_A2DP_CODEC_INDEX_SOURCE_AAC] = true;
       } else if (strcmp(tok, "aptx") == 0) {
-        LOG_INFO(LOG_TAG, "%s: APTX offload supported", __func__);
+        LOG_INFO("%s: APTX offload supported", __func__);
         offload_codec_support[BTAV_A2DP_CODEC_INDEX_SOURCE_APTX] = true;
       } else if (strcmp(tok, "aptxhd") == 0) {
-        LOG_INFO(LOG_TAG, "%s: APTXHD offload supported", __func__);
+        LOG_INFO("%s: APTXHD offload supported", __func__);
         offload_codec_support[BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD] = true;
       } else if (strcmp(tok, "ldac") == 0) {
-        LOG_INFO(LOG_TAG, "%s: LDAC offload supported", __func__);
+        LOG_INFO("%s: LDAC offload supported", __func__);
         offload_codec_support[BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC] = true;
       }
       tok = strtok_r(NULL, "-", &tmp_token);
@@ -616,7 +616,7 @@
     if (codec_config == nullptr) continue;
 
     if (codec_priority != BTAV_A2DP_CODEC_PRIORITY_DEFAULT) {
-      LOG_INFO(LOG_TAG, "%s: updated %s codec priority to %d", __func__,
+      LOG_INFO("%s: updated %s codec priority to %d", __func__,
                codec_config->name().c_str(), codec_priority);
     }
 
@@ -638,19 +638,18 @@
   }
 
   if (ordered_source_codecs_.empty()) {
-    LOG_ERROR(LOG_TAG, "%s: no Source codecs were initialized", __func__);
+    LOG_ERROR("%s: no Source codecs were initialized", __func__);
   } else {
     for (auto iter : ordered_source_codecs_) {
-      LOG_INFO(LOG_TAG, "%s: initialized Source codec %s", __func__,
+      LOG_INFO("%s: initialized Source codec %s", __func__,
                iter->name().c_str());
     }
   }
   if (ordered_sink_codecs_.empty()) {
-    LOG_ERROR(LOG_TAG, "%s: no Sink codecs were initialized", __func__);
+    LOG_ERROR("%s: no Sink codecs were initialized", __func__);
   } else {
     for (auto iter : ordered_sink_codecs_) {
-      LOG_INFO(LOG_TAG, "%s: initialized Sink codec %s", __func__,
-               iter->name().c_str());
+      LOG_INFO("%s: initialized Sink codec %s", __func__, iter->name().c_str());
     }
   }
 
@@ -730,7 +729,7 @@
   *p_restart_output = false;
   *p_config_updated = false;
 
-  LOG_DEBUG(LOG_TAG, "%s: Configuring: %s", __func__,
+  LOG_DEBUG("%s: Configuring: %s", __func__,
             codec_user_config.ToString().c_str());
 
   if (codec_user_config.codec_type < BTAV_A2DP_CODEC_INDEX_MAX) {
@@ -810,10 +809,10 @@
 
   if (*p_restart_input || *p_restart_output) *p_config_updated = true;
 
-  LOG_DEBUG(LOG_TAG,
-            "%s: Configured: restart_input = %d restart_output = %d "
-            "config_updated = %d",
-            __func__, *p_restart_input, *p_restart_output, *p_config_updated);
+  LOG_DEBUG(
+      "%s: Configured: restart_input = %d restart_output = %d "
+      "config_updated = %d",
+      __func__, *p_restart_input, *p_restart_output, *p_config_updated);
 
   return true;
 
@@ -868,11 +867,11 @@
   if (current_codec_config_ != nullptr) {
     codec_user_config = current_codec_config_->getCodecUserConfig();
     if (!A2dpCodecConfig::isCodecConfigEmpty(codec_user_config)) {
-      LOG_WARN(LOG_TAG,
-               "%s: ignoring peer OTA configuration for codec %s: "
-               "existing user configuration for current codec %s",
-               __func__, A2DP_CodecName(p_ota_codec_config),
-               current_codec_config_->name().c_str());
+      LOG_WARN(
+          "%s: ignoring peer OTA configuration for codec %s: "
+          "existing user configuration for current codec %s",
+          __func__, A2DP_CodecName(p_ota_codec_config),
+          current_codec_config_->name().c_str());
       goto fail;
     }
   }
@@ -882,16 +881,15 @@
   // ignored.
   codec_type = A2DP_SourceCodecIndex(p_ota_codec_config);
   if (codec_type == BTAV_A2DP_CODEC_INDEX_MAX) {
-    LOG_WARN(LOG_TAG,
-             "%s: ignoring peer OTA codec configuration: "
-             "invalid codec",
-             __func__);
+    LOG_WARN(
+        "%s: ignoring peer OTA codec configuration: "
+        "invalid codec",
+        __func__);
     goto fail;  // Invalid codec
   } else {
     auto iter = indexed_codecs_.find(codec_type);
     if (iter == indexed_codecs_.end()) {
-      LOG_WARN(LOG_TAG,
-               "%s: cannot find codec configuration for peer OTA codec %s",
+      LOG_WARN("%s: cannot find codec configuration for peer OTA codec %s",
                __func__, A2DP_CodecName(p_ota_codec_config));
       goto fail;
     }
@@ -900,10 +898,10 @@
   if (a2dp_codec_config == nullptr) goto fail;
   codec_user_config = a2dp_codec_config->getCodecUserConfig();
   if (!A2dpCodecConfig::isCodecConfigEmpty(codec_user_config)) {
-    LOG_WARN(LOG_TAG,
-             "%s: ignoring peer OTA configuration for codec %s: "
-             "existing user configuration for same codec",
-             __func__, A2DP_CodecName(p_ota_codec_config));
+    LOG_WARN(
+        "%s: ignoring peer OTA configuration for codec %s: "
+        "existing user configuration for same codec",
+        __func__, A2DP_CodecName(p_ota_codec_config));
     goto fail;
   }
   current_codec_config_ = a2dp_codec_config;
@@ -914,8 +912,7 @@
           codec_user_config, codec_audio_config, p_peer_params,
           p_ota_codec_config, false, p_result_codec_config, p_restart_input,
           p_restart_output, p_config_updated)) {
-    LOG_WARN(LOG_TAG,
-             "%s: cannot set codec configuration for peer OTA codec %s",
+    LOG_WARN("%s: cannot set codec configuration for peer OTA codec %s",
              __func__, A2DP_CodecName(p_ota_codec_config));
     goto fail;
   }
@@ -1014,7 +1011,7 @@
 bool A2DP_IsSourceCodecValid(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1033,7 +1030,7 @@
 bool A2DP_IsSinkCodecValid(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1052,7 +1049,7 @@
 bool A2DP_IsPeerSourceCodecValid(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1071,7 +1068,7 @@
 bool A2DP_IsPeerSinkCodecValid(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1090,7 +1087,7 @@
 bool A2DP_IsSinkCodecSupported(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1103,14 +1100,14 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return false;
 }
 
 bool A2DP_IsPeerSourceCodecSupported(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1123,7 +1120,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return false;
 }
 
@@ -1148,7 +1145,7 @@
 const char* A2DP_CodecName(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1161,7 +1158,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return "UNKNOWN CODEC";
 }
 
@@ -1183,7 +1180,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type_a);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type_a);
   return false;
 }
 
@@ -1205,14 +1202,14 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type_a);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type_a);
   return false;
 }
 
 int A2DP_GetTrackSampleRate(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1225,14 +1222,14 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return -1;
 }
 
 int A2DP_GetTrackBitsPerSample(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1245,14 +1242,14 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return -1;
 }
 
 int A2DP_GetTrackChannelCount(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1265,14 +1262,14 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return -1;
 }
 
 int A2DP_GetSinkTrackChannelType(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1285,7 +1282,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return -1;
 }
 
@@ -1304,7 +1301,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return false;
 }
 
@@ -1324,7 +1321,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return false;
 }
 
@@ -1332,7 +1329,7 @@
     const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1345,7 +1342,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return NULL;
 }
 
@@ -1353,7 +1350,7 @@
     const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1366,7 +1363,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return NULL;
 }
 
@@ -1384,14 +1381,14 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return false;
 }
 
 btav_a2dp_codec_index_t A2DP_SourceCodecIndex(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1404,14 +1401,14 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return BTAV_A2DP_CODEC_INDEX_MAX;
 }
 
 btav_a2dp_codec_index_t A2DP_SinkCodecIndex(const uint8_t* p_codec_info) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
 
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
 
   switch (codec_type) {
     case A2DP_MEDIA_CT_SBC:
@@ -1424,7 +1421,7 @@
       break;
   }
 
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  LOG_ERROR("%s: unsupported codec type 0x%x", __func__, codec_type);
   return BTAV_A2DP_CODEC_INDEX_MAX;
 }
 
@@ -1450,8 +1447,7 @@
 
 bool A2DP_InitCodecConfig(btav_a2dp_codec_index_t codec_index,
                           AvdtpSepConfig* p_cfg) {
-  LOG_VERBOSE(LOG_TAG, "%s: codec %s", __func__,
-              A2DP_CodecIndexStr(codec_index));
+  LOG_VERBOSE("%s: codec %s", __func__, A2DP_CodecIndexStr(codec_index));
 
   /* Default: no content protection info */
   p_cfg->num_protect = 0;
diff --git a/stack/a2dp/a2dp_sbc.cc b/stack/a2dp/a2dp_sbc.cc
index 4c48993..adf9cc5 100644
--- a/stack/a2dp/a2dp_sbc.cc
+++ b/stack/a2dp/a2dp_sbc.cc
@@ -103,8 +103,12 @@
 };
 
 static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_sbc = {
-    a2dp_sbc_decoder_init, a2dp_sbc_decoder_cleanup,
+    a2dp_sbc_decoder_init,
+    a2dp_sbc_decoder_cleanup,
     a2dp_sbc_decoder_decode_packet,
+    nullptr,  // decoder_start
+    nullptr,  // decoder_suspend
+    nullptr,  // decoder_configure
 };
 
 static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilitySbc(
@@ -330,7 +334,7 @@
 void A2DP_InitDefaultCodecSbc(uint8_t* p_codec_info) {
   if (A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &a2dp_sbc_default_config,
                         p_codec_info) != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: A2DP_BuildInfoSbc failed", __func__);
+    LOG_ERROR("%s: A2DP_BuildInfoSbc failed", __func__);
   }
 }
 
@@ -349,25 +353,25 @@
   /* parse configuration */
   status = A2DP_ParseInfoSbc(&cfg_cie, p_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: parsing failed %d", __func__, status);
+    LOG_ERROR("%s: parsing failed %d", __func__, status);
     return status;
   }
 
   /* verify that each parameter is in range */
 
-  LOG_VERBOSE(LOG_TAG, "%s: FREQ peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: FREQ peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.samp_freq, p_cap->samp_freq);
-  LOG_VERBOSE(LOG_TAG, "%s: CH_MODE peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: CH_MODE peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.ch_mode, p_cap->ch_mode);
-  LOG_VERBOSE(LOG_TAG, "%s: BLOCK_LEN peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: BLOCK_LEN peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.block_len, p_cap->block_len);
-  LOG_VERBOSE(LOG_TAG, "%s: SUB_BAND peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: SUB_BAND peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.num_subbands, p_cap->num_subbands);
-  LOG_VERBOSE(LOG_TAG, "%s: ALLOC_METHOD peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: ALLOC_METHOD peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.alloc_method, p_cap->alloc_method);
-  LOG_VERBOSE(LOG_TAG, "%s: MIN_BitPool peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: MIN_BitPool peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.min_bitpool, p_cap->min_bitpool);
-  LOG_VERBOSE(LOG_TAG, "%s: MAX_BitPool peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: MAX_BitPool peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.max_bitpool, p_cap->max_bitpool);
 
   /* sampling frequency */
@@ -405,14 +409,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoSbc(&sbc_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoSbc(&sbc_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -431,14 +433,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoSbc(&sbc_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoSbc(&sbc_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -462,8 +462,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -488,8 +487,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -502,8 +500,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -526,8 +523,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -548,8 +544,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -574,8 +569,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -596,8 +590,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -622,8 +615,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -648,8 +640,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -661,8 +652,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -675,8 +665,7 @@
 
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -800,8 +789,8 @@
 
   // Updated the max bitpool
   if (cfg_cie.max_bitpool > A2DP_SBC_MAX_BITPOOL) {
-    LOG_WARN(LOG_TAG, "%s: Updated the SBC codec max bitpool from %d to %d",
-             __func__, cfg_cie.max_bitpool, A2DP_SBC_MAX_BITPOOL);
+    LOG_WARN("%s: Updated the SBC codec max bitpool from %d to %d", __func__,
+             cfg_cie.max_bitpool, A2DP_SBC_MAX_BITPOOL);
     cfg_cie.max_bitpool = A2DP_SBC_MAX_BITPOOL;
   }
 
@@ -901,7 +890,7 @@
 
   // Load the encoder
   if (!A2DP_LoadEncoderSbc()) {
-    LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
+    LOG_ERROR("%s: cannot load the encoder", __func__);
     return false;
   }
 
@@ -1102,8 +1091,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoSbc(&peer_info_cie, p_peer_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
   // Try using the prefered peer codec config (if valid), instead of the peer
@@ -1205,10 +1194,10 @@
     }
   } while (false);
   if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match sample frequency: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, p_a2dp_sbc_caps->samp_freq, peer_info_cie.samp_freq);
+    LOG_ERROR(
+        "%s: cannot match sample frequency: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, p_a2dp_sbc_caps->samp_freq, peer_info_cie.samp_freq);
     goto fail;
   }
 
@@ -1260,8 +1249,7 @@
     }
   } while (false);
   if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match bits per sample: user preference = 0x%x",
+    LOG_ERROR("%s: cannot match bits per sample: user preference = 0x%x",
               __func__, codec_user_config_.bits_per_sample);
     goto fail;
   }
@@ -1354,10 +1342,10 @@
     }
   } while (false);
   if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match channel mode: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, p_a2dp_sbc_caps->ch_mode, peer_info_cie.ch_mode);
+    LOG_ERROR(
+        "%s: cannot match channel mode: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, p_a2dp_sbc_caps->ch_mode, peer_info_cie.ch_mode);
     goto fail;
   }
 
@@ -1374,10 +1362,10 @@
   } else if (block_len & A2DP_SBC_IE_BLOCKS_4) {
     result_config_cie.block_len = A2DP_SBC_IE_BLOCKS_4;
   } else {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match block length: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, p_a2dp_sbc_caps->block_len, peer_info_cie.block_len);
+    LOG_ERROR(
+        "%s: cannot match block length: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, p_a2dp_sbc_caps->block_len, peer_info_cie.block_len);
     goto fail;
   }
 
@@ -1390,11 +1378,10 @@
   } else if (num_subbands & A2DP_SBC_IE_SUBBAND_4) {
     result_config_cie.num_subbands = A2DP_SBC_IE_SUBBAND_4;
   } else {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match number of sub-bands: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, p_a2dp_sbc_caps->num_subbands,
-              peer_info_cie.num_subbands);
+    LOG_ERROR(
+        "%s: cannot match number of sub-bands: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, p_a2dp_sbc_caps->num_subbands, peer_info_cie.num_subbands);
     goto fail;
   }
 
@@ -1407,11 +1394,10 @@
   } else if (alloc_method & A2DP_SBC_IE_ALLOC_MD_S) {
     result_config_cie.alloc_method = A2DP_SBC_IE_ALLOC_MD_S;
   } else {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match allocation method: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, p_a2dp_sbc_caps->alloc_method,
-              peer_info_cie.alloc_method);
+    LOG_ERROR(
+        "%s: cannot match allocation method: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, p_a2dp_sbc_caps->alloc_method, peer_info_cie.alloc_method);
     goto fail;
   }
 
@@ -1425,12 +1411,11 @@
   if (result_config_cie.max_bitpool > peer_info_cie.max_bitpool)
     result_config_cie.max_bitpool = peer_info_cie.max_bitpool;
   if (result_config_cie.min_bitpool > result_config_cie.max_bitpool) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match min/max bitpool: "
-              "local caps min/max = 0x%x/0x%x peer info min/max = 0x%x/0x%x",
-              __func__, p_a2dp_sbc_caps->min_bitpool,
-              p_a2dp_sbc_caps->max_bitpool, peer_info_cie.min_bitpool,
-              peer_info_cie.max_bitpool);
+    LOG_ERROR(
+        "%s: cannot match min/max bitpool: "
+        "local caps min/max = 0x%x/0x%x peer info min/max = 0x%x/0x%x",
+        __func__, p_a2dp_sbc_caps->min_bitpool, p_a2dp_sbc_caps->max_bitpool,
+        peer_info_cie.min_bitpool, peer_info_cie.max_bitpool);
     goto fail;
   }
 
@@ -1500,8 +1485,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoSbc(&peer_info_cie, p_peer_codec_capabilities, true);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
 
@@ -1565,7 +1550,7 @@
 
   // Load the decoder
   if (!A2DP_LoadDecoderSbc()) {
-    LOG_ERROR(LOG_TAG, "%s: cannot load the decoder", __func__);
+    LOG_ERROR("%s: cannot load the decoder", __func__);
     return false;
   }
 
diff --git a/stack/a2dp/a2dp_sbc_decoder.cc b/stack/a2dp/a2dp_sbc_decoder.cc
index bc64513..b275c74 100644
--- a/stack/a2dp/a2dp_sbc_decoder.cc
+++ b/stack/a2dp/a2dp_sbc_decoder.cc
@@ -45,8 +45,7 @@
       &a2dp_sbc_decoder_cb.decoder_context, a2dp_sbc_decoder_cb.context_data,
       sizeof(a2dp_sbc_decoder_cb.context_data), 2, 2, false);
   if (!OI_SUCCESS(status)) {
-    LOG_ERROR(LOG_TAG,
-              "%s: OI_CODEC_SBC_DecoderReset failed with error code %d",
+    LOG_ERROR("%s: OI_CODEC_SBC_DecoderReset failed with error code %d",
               __func__, status);
     return false;
   }
@@ -64,7 +63,7 @@
   size_t data_size = p_buf->len;
 
   if (data_size == 0) {
-    LOG_ERROR(LOG_TAG, "%s: Empty packet", __func__);
+    LOG_ERROR("%s: Empty packet", __func__);
     return false;
   }
   size_t num_frames = data[0] & 0xf;
@@ -82,7 +81,7 @@
         OI_CODEC_SBC_DecodeFrame(&a2dp_sbc_decoder_cb.decoder_context, &oi_data,
                                  &oi_size, out_ptr, &out_size);
     if (!OI_SUCCESS(status)) {
-      LOG_ERROR(LOG_TAG, "%s: Decoding failure: %d", __func__, status);
+      LOG_ERROR("%s: Decoding failure: %d", __func__, status);
       return false;
     }
     out_avail -= out_size;
diff --git a/stack/a2dp/a2dp_sbc_encoder.cc b/stack/a2dp/a2dp_sbc_encoder.cc
index aac7e6d..0a8778c 100644
--- a/stack/a2dp/a2dp_sbc_encoder.cc
+++ b/stack/a2dp/a2dp_sbc_encoder.cc
@@ -22,6 +22,7 @@
 #include "a2dp_sbc_encoder.h"
 
 #include <limits.h>
+#include <math.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -71,8 +72,8 @@
   int32_t aa_feed_counter;
   int32_t aa_feed_residue;
   uint32_t counter;
-  uint32_t bytes_per_tick; /* pcm bytes read each media task tick */
-  uint64_t last_frame_us;
+  uint32_t bytes_per_tick;              // pcm bytes read each media task tick
+  uint64_t last_frame_timestamp_100ns;  // values in 1/10 microseconds
 } tA2DP_SBC_FEEDING_STATE;
 
 typedef struct {
@@ -166,10 +167,10 @@
   a2dp_sbc_encoder_cb.timestamp = 0;
 
   if (a2dp_sbc_encoder_cb.peer_mtu == 0) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid peer MTU",
-              __func__, name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid peer MTU",
+        __func__, name().c_str());
     return false;
   }
 
@@ -200,10 +201,10 @@
   *p_restart_output = false;
   *p_config_updated = false;
   if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid codec config",
-              __func__, a2dp_codec_config->name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid codec config",
+        __func__, a2dp_codec_config->name().c_str());
     return;
   }
   const uint8_t* p_codec_info = codec_info;
@@ -216,9 +217,9 @@
   p_feeding_params->bits_per_sample =
       a2dp_codec_config->getAudioBitsPerSample();
   p_feeding_params->channel_count = A2DP_GetTrackChannelCountSbc(p_codec_info);
-  LOG_DEBUG(LOG_TAG, "%s: sample_rate=%u bits_per_sample=%u channel_count=%u",
-            __func__, p_feeding_params->sample_rate,
-            p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
+  LOG_DEBUG("%s: sample_rate=%u bits_per_sample=%u channel_count=%u", __func__,
+            p_feeding_params->sample_rate, p_feeding_params->bits_per_sample,
+            p_feeding_params->channel_count);
   a2dp_sbc_feeding_reset();
 
   // The codec parameters
@@ -235,18 +236,18 @@
 
   // Reset invalid parameters
   if (!p_encoder_params->s16NumOfSubBands) {
-    LOG_WARN(LOG_TAG, "%s: SubBands are set to 0, resetting to max (%d)",
-             __func__, SBC_MAX_NUM_OF_SUBBANDS);
+    LOG_WARN("%s: SubBands are set to 0, resetting to max (%d)", __func__,
+             SBC_MAX_NUM_OF_SUBBANDS);
     p_encoder_params->s16NumOfSubBands = SBC_MAX_NUM_OF_SUBBANDS;
   }
   if (!p_encoder_params->s16NumOfBlocks) {
-    LOG_WARN(LOG_TAG, "%s: Blocks are set to 0, resetting to max (%d)",
-             __func__, SBC_MAX_NUM_OF_BLOCKS);
+    LOG_WARN("%s: Blocks are set to 0, resetting to max (%d)", __func__,
+             SBC_MAX_NUM_OF_BLOCKS);
     p_encoder_params->s16NumOfBlocks = SBC_MAX_NUM_OF_BLOCKS;
   }
   if (!p_encoder_params->s16NumOfChannels) {
-    LOG_WARN(LOG_TAG, "%s: Channels are set to 0, resetting to max (%d)",
-             __func__, SBC_MAX_NUM_OF_CHANNELS);
+    LOG_WARN("%s: Channels are set to 0, resetting to max (%d)", __func__,
+             SBC_MAX_NUM_OF_CHANNELS);
     p_encoder_params->s16NumOfChannels = SBC_MAX_NUM_OF_CHANNELS;
   }
 
@@ -269,17 +270,16 @@
   // Set the initial target bit rate
   p_encoder_params->u16BitRate = a2dp_sbc_source_rate();
 
-  LOG_DEBUG(LOG_TAG, "%s: MTU=%d, peer_mtu=%d min_bitpool=%d max_bitpool=%d",
-            __func__, a2dp_sbc_encoder_cb.TxAaMtuSize, peer_mtu, min_bitpool,
+  LOG_DEBUG("%s: MTU=%d, peer_mtu=%d min_bitpool=%d max_bitpool=%d", __func__,
+            a2dp_sbc_encoder_cb.TxAaMtuSize, peer_mtu, min_bitpool,
             max_bitpool);
-  LOG_DEBUG(LOG_TAG,
-            "%s: ChannelMode=%d, NumOfSubBands=%d, NumOfBlocks=%d, "
-            "AllocationMethod=%d, BitRate=%d, SamplingFreq=%d BitPool=%d",
-            __func__, p_encoder_params->s16ChannelMode,
-            p_encoder_params->s16NumOfSubBands,
-            p_encoder_params->s16NumOfBlocks,
-            p_encoder_params->s16AllocationMethod, p_encoder_params->u16BitRate,
-            s16SamplingFreq, p_encoder_params->s16BitPool);
+  LOG_DEBUG(
+      "%s: ChannelMode=%d, NumOfSubBands=%d, NumOfBlocks=%d, "
+      "AllocationMethod=%d, BitRate=%d, SamplingFreq=%d BitPool=%d",
+      __func__, p_encoder_params->s16ChannelMode,
+      p_encoder_params->s16NumOfSubBands, p_encoder_params->s16NumOfBlocks,
+      p_encoder_params->s16AllocationMethod, p_encoder_params->u16BitRate,
+      s16SamplingFreq, p_encoder_params->s16BitPool);
 
   do {
     if ((p_encoder_params->s16ChannelMode == SBC_JOINT_STEREO) ||
@@ -329,19 +329,17 @@
 
     if (s16BitPool < 0) s16BitPool = 0;
 
-    LOG_DEBUG(LOG_TAG, "%s: bitpool candidate: %d (%d kbps)", __func__,
-              s16BitPool, p_encoder_params->u16BitRate);
+    LOG_DEBUG("%s: bitpool candidate: %d (%d kbps)", __func__, s16BitPool,
+              p_encoder_params->u16BitRate);
 
     if (s16BitPool > max_bitpool) {
-      LOG_DEBUG(LOG_TAG, "%s: computed bitpool too large (%d)", __func__,
-                s16BitPool);
+      LOG_DEBUG("%s: computed bitpool too large (%d)", __func__, s16BitPool);
       /* Decrease bitrate */
       p_encoder_params->u16BitRate -= A2DP_SBC_BITRATE_STEP;
       /* Record that we have decreased the bitrate */
       protect |= 1;
     } else if (s16BitPool < min_bitpool) {
-      LOG_WARN(LOG_TAG, "%s: computed bitpool too small (%d)", __func__,
-               s16BitPool);
+      LOG_WARN("%s: computed bitpool too small (%d)", __func__, s16BitPool);
 
       /* Increase bitrate */
       uint16_t previous_u16BitRate = p_encoder_params->u16BitRate;
@@ -355,7 +353,7 @@
     }
     /* In case we have already increased and decreased the bitrate, just stop */
     if (protect == 3) {
-      LOG_ERROR(LOG_TAG, "%s: could not find bitpool in range", __func__);
+      LOG_ERROR("%s: could not find bitpool in range", __func__);
       break;
     }
   } while (true);
@@ -363,7 +361,7 @@
   /* Finally update the bitpool in the encoder structure */
   p_encoder_params->s16BitPool = s16BitPool;
 
-  LOG_DEBUG(LOG_TAG, "%s: final bit rate %d, final bit pool %d", __func__,
+  LOG_DEBUG("%s: final bit rate %d, final bit pool %d", __func__,
             p_encoder_params->u16BitRate, p_encoder_params->s16BitPool);
 
   /* Reset the SBC encoder */
@@ -387,7 +385,7 @@
        A2DP_SBC_ENCODER_INTERVAL_MS) /
       1000;
 
-  LOG_DEBUG(LOG_TAG, "%s: PCM bytes per tick %u", __func__,
+  LOG_DEBUG("%s: PCM bytes per tick %u", __func__,
             a2dp_sbc_encoder_cb.feeding_state.bytes_per_tick);
 }
 
@@ -405,8 +403,8 @@
   uint8_t nb_iterations = 0;
 
   a2dp_sbc_get_num_frame_iteration(&nb_iterations, &nb_frame, timestamp_us);
-  LOG_VERBOSE(LOG_TAG, "%s: Sending %d frames per iteration, %d iterations",
-              __func__, nb_frame, nb_iterations);
+  LOG_VERBOSE("%s: Sending %d frames per iteration, %d iterations", __func__,
+              nb_frame, nb_iterations);
   if (nb_frame == 0) return;
 
   for (uint8_t counter = 0; counter < nb_iterations; counter++) {
@@ -430,18 +428,31 @@
       a2dp_sbc_encoder_cb.sbc_encoder_params.s16NumOfBlocks *
       a2dp_sbc_encoder_cb.feeding_params.channel_count *
       a2dp_sbc_encoder_cb.feeding_params.bits_per_sample / 8;
-  LOG_VERBOSE(LOG_TAG, "%s: pcm_bytes_per_frame %u", __func__,
-              pcm_bytes_per_frame);
+  LOG_VERBOSE("%s: pcm_bytes_per_frame %u", __func__, pcm_bytes_per_frame);
 
-  uint32_t us_this_tick = A2DP_SBC_ENCODER_INTERVAL_MS * 1000;
-  uint64_t now_us = timestamp_us;
-  if (a2dp_sbc_encoder_cb.feeding_state.last_frame_us != 0)
-    us_this_tick = (now_us - a2dp_sbc_encoder_cb.feeding_state.last_frame_us);
-  a2dp_sbc_encoder_cb.feeding_state.last_frame_us = now_us;
+  uint32_t hecto_ns_this_tick = A2DP_SBC_ENCODER_INTERVAL_MS * 10000;
+  uint64_t* last_100ns =
+      &a2dp_sbc_encoder_cb.feeding_state.last_frame_timestamp_100ns;
+  uint64_t now_100ns = timestamp_us * 10;
+  if (*last_100ns != 0) {
+    hecto_ns_this_tick = (now_100ns - *last_100ns);
+  }
+  *last_100ns = now_100ns;
 
-  a2dp_sbc_encoder_cb.feeding_state.counter +=
-      a2dp_sbc_encoder_cb.feeding_state.bytes_per_tick * us_this_tick /
-      (A2DP_SBC_ENCODER_INTERVAL_MS * 1000);
+  uint32_t bytes_this_tick = a2dp_sbc_encoder_cb.feeding_state.bytes_per_tick *
+                             hecto_ns_this_tick /
+                             (A2DP_SBC_ENCODER_INTERVAL_MS * 10000);
+  a2dp_sbc_encoder_cb.feeding_state.counter += bytes_this_tick;
+  // Without this erratum, there was a three microseocnd shift per tick which
+  // would cause one SBC frame mismatched after every 20 seconds
+  uint32_t erratum_100ns =
+      ceil(1.0f * A2DP_SBC_ENCODER_INTERVAL_MS * 10000 * bytes_this_tick /
+           a2dp_sbc_encoder_cb.feeding_state.bytes_per_tick);
+  if (erratum_100ns < hecto_ns_this_tick) {
+    LOG_VERBOSE("%s: hecto_ns_this_tick=%d, bytes=%d, erratum_100ns=%d",
+                __func__, hecto_ns_this_tick, bytes_this_tick, erratum_100ns);
+    *last_100ns -= hecto_ns_this_tick - erratum_100ns;
+  }
 
   /* Calculate the number of frames pending for this media tick */
   projected_nof =
@@ -450,7 +461,7 @@
   a2dp_sbc_encoder_cb.stats.media_read_total_expected_frames += projected_nof;
 
   if (projected_nof > MAX_PCM_FRAME_NUM_PER_TICK) {
-    LOG_WARN(LOG_TAG, "%s: limiting frames to be sent from %d to %d", __func__,
+    LOG_WARN("%s: limiting frames to be sent from %d to %d", __func__,
              projected_nof, MAX_PCM_FRAME_NUM_PER_TICK);
 
     // Update the stats
@@ -460,20 +471,17 @@
     projected_nof = MAX_PCM_FRAME_NUM_PER_TICK;
   }
 
-  LOG_VERBOSE(LOG_TAG, "%s: frames for available PCM data %u", __func__,
-              projected_nof);
+  LOG_VERBOSE("%s: frames for available PCM data %u", __func__, projected_nof);
 
   if (a2dp_sbc_encoder_cb.is_peer_edr) {
     if (!a2dp_sbc_encoder_cb.tx_sbc_frames) {
-      LOG_ERROR(LOG_TAG, "%s: tx_sbc_frames not updated, update from here",
-                __func__);
+      LOG_ERROR("%s: tx_sbc_frames not updated, update from here", __func__);
       a2dp_sbc_encoder_cb.tx_sbc_frames = calculate_max_frames_per_packet();
     }
 
     nof = a2dp_sbc_encoder_cb.tx_sbc_frames;
     if (!nof) {
-      LOG_ERROR(LOG_TAG,
-                "%s: number of frames not updated, set calculated values",
+      LOG_ERROR("%s: number of frames not updated, set calculated values",
                 __func__);
       nof = projected_nof;
       noi = 1;
@@ -481,8 +489,8 @@
       if (nof < projected_nof) {
         noi = projected_nof / nof;  // number of iterations would vary
         if (noi > A2DP_SBC_MAX_PCM_ITER_NUM_PER_TICK) {
-          LOG_ERROR(LOG_TAG, "%s: Audio Congestion (iterations:%d > max (%d))",
-                    __func__, noi, A2DP_SBC_MAX_PCM_ITER_NUM_PER_TICK);
+          LOG_ERROR("%s: Audio Congestion (iterations:%d > max (%d))", __func__,
+                    noi, A2DP_SBC_MAX_PCM_ITER_NUM_PER_TICK);
           noi = A2DP_SBC_MAX_PCM_ITER_NUM_PER_TICK;
           a2dp_sbc_encoder_cb.feeding_state.counter =
               noi * nof * pcm_bytes_per_frame;
@@ -490,17 +498,16 @@
         projected_nof = nof;
       } else {
         noi = 1;  // number of iterations is 1
-        LOG_VERBOSE(LOG_TAG, "%s: reducing frames for available PCM data",
-                    __func__);
+        LOG_VERBOSE("%s: reducing frames for available PCM data", __func__);
         nof = projected_nof;
       }
     }
   } else {
     // For BR cases nof will be same as the value retrieved at projected_nof
-    LOG_VERBOSE(LOG_TAG, "%s: headset BR, number of frames %u", __func__, nof);
+    LOG_VERBOSE("%s: headset BR, number of frames %u", __func__, nof);
     if (projected_nof > MAX_PCM_FRAME_NUM_PER_TICK) {
-      LOG_ERROR(LOG_TAG, "%s: Audio Congestion (frames: %d > max (%d))",
-                __func__, projected_nof, MAX_PCM_FRAME_NUM_PER_TICK);
+      LOG_ERROR("%s: Audio Congestion (frames: %d > max (%d))", __func__,
+                projected_nof, MAX_PCM_FRAME_NUM_PER_TICK);
 
       // Update the stats
       size_t delta = projected_nof - MAX_PCM_FRAME_NUM_PER_TICK;
@@ -513,8 +520,8 @@
     nof = projected_nof;
   }
   a2dp_sbc_encoder_cb.feeding_state.counter -= noi * nof * pcm_bytes_per_frame;
-  LOG_VERBOSE(LOG_TAG, "%s: effective num of frames %u, iterations %u",
-              __func__, nof, noi);
+  LOG_VERBOSE("%s: effective num of frames %u, iterations %u", __func__, nof,
+              noi);
 
   *num_of_frames = nof;
   *num_of_iterations = noi;
@@ -558,7 +565,7 @@
 
         bytes_read += num_bytes;
       } else {
-        LOG_WARN(LOG_TAG, "%s: underflow %d, %d", __func__, nb_frame,
+        LOG_WARN("%s: underflow %d, %d", __func__, nb_frame,
                  a2dp_sbc_encoder_cb.feeding_state.aa_feed_residue);
         a2dp_sbc_encoder_cb.feeding_state.counter +=
             nb_frame * p_encoder_params->s16NumOfSubBands *
@@ -757,19 +764,18 @@
   uint16_t result = 0;
   uint32_t frame_len;
 
-  LOG_VERBOSE(LOG_TAG, "%s: original AVDTP MTU size: %d", __func__,
+  LOG_VERBOSE("%s: original AVDTP MTU size: %d", __func__,
               a2dp_sbc_encoder_cb.TxAaMtuSize);
   if (a2dp_sbc_encoder_cb.is_peer_edr &&
       !a2dp_sbc_encoder_cb.peer_supports_3mbps) {
     // This condition would be satisfied only if the remote device is
     // EDR and supports only 2 Mbps, but the effective AVDTP MTU size
     // exceeds the 2DH5 packet size.
-    LOG_VERBOSE(LOG_TAG,
-                "%s: The remote device is EDR but does not support 3 Mbps",
+    LOG_VERBOSE("%s: The remote device is EDR but does not support 3 Mbps",
                 __func__);
 
     if (effective_mtu_size > MAX_2MBPS_AVDTP_MTU) {
-      LOG_WARN(LOG_TAG, "%s: Restricting AVDTP MTU size to %d", __func__,
+      LOG_WARN("%s: Restricting AVDTP MTU size to %d", __func__,
                MAX_2MBPS_AVDTP_MTU);
       effective_mtu_size = MAX_2MBPS_AVDTP_MTU;
       a2dp_sbc_encoder_cb.TxAaMtuSize = effective_mtu_size;
@@ -777,53 +783,49 @@
   }
 
   if (!p_encoder_params->s16NumOfSubBands) {
-    LOG_ERROR(LOG_TAG, "%s: SubBands are set to 0, resetting to %d", __func__,
+    LOG_ERROR("%s: SubBands are set to 0, resetting to %d", __func__,
               SBC_MAX_NUM_OF_SUBBANDS);
     p_encoder_params->s16NumOfSubBands = SBC_MAX_NUM_OF_SUBBANDS;
   }
   if (!p_encoder_params->s16NumOfBlocks) {
-    LOG_ERROR(LOG_TAG, "%s: Blocks are set to 0, resetting to %d", __func__,
+    LOG_ERROR("%s: Blocks are set to 0, resetting to %d", __func__,
               SBC_MAX_NUM_OF_BLOCKS);
     p_encoder_params->s16NumOfBlocks = SBC_MAX_NUM_OF_BLOCKS;
   }
   if (!p_encoder_params->s16NumOfChannels) {
-    LOG_ERROR(LOG_TAG, "%s: Channels are set to 0, resetting to %d", __func__,
+    LOG_ERROR("%s: Channels are set to 0, resetting to %d", __func__,
               SBC_MAX_NUM_OF_CHANNELS);
     p_encoder_params->s16NumOfChannels = SBC_MAX_NUM_OF_CHANNELS;
   }
 
   frame_len = a2dp_sbc_frame_length();
 
-  LOG_VERBOSE(LOG_TAG, "%s: Effective Tx MTU to be considered: %d", __func__,
+  LOG_VERBOSE("%s: Effective Tx MTU to be considered: %d", __func__,
               effective_mtu_size);
 
   switch (p_encoder_params->s16SamplingFreq) {
     case SBC_sf44100:
       if (frame_len == 0) {
-        LOG_ERROR(LOG_TAG,
-                  "%s: Calculating frame length, resetting it to default %d",
+        LOG_ERROR("%s: Calculating frame length, resetting it to default %d",
                   __func__, A2DP_SBC_MAX_HQ_FRAME_SIZE_44_1);
         frame_len = A2DP_SBC_MAX_HQ_FRAME_SIZE_44_1;
       }
       result = (effective_mtu_size - A2DP_HDR_SIZE) / frame_len;
-      LOG_VERBOSE(LOG_TAG, "%s: Max number of SBC frames: %d", __func__,
-                  result);
+      LOG_VERBOSE("%s: Max number of SBC frames: %d", __func__, result);
       break;
 
     case SBC_sf48000:
       if (frame_len == 0) {
-        LOG_ERROR(LOG_TAG,
-                  "%s: Calculating frame length, resetting it to default %d",
+        LOG_ERROR("%s: Calculating frame length, resetting it to default %d",
                   __func__, A2DP_SBC_MAX_HQ_FRAME_SIZE_48);
         frame_len = A2DP_SBC_MAX_HQ_FRAME_SIZE_48;
       }
       result = (effective_mtu_size - A2DP_HDR_SIZE) / frame_len;
-      LOG_VERBOSE(LOG_TAG, "%s: Max number of SBC frames: %d", __func__,
-                  result);
+      LOG_VERBOSE("%s: Max number of SBC frames: %d", __func__, result);
       break;
 
     default:
-      LOG_ERROR(LOG_TAG, "%s: Max number of SBC frames: %d", __func__, result);
+      LOG_ERROR("%s: Max number of SBC frames: %d", __func__, result);
       break;
   }
   return result;
@@ -835,8 +837,8 @@
   /* restrict bitrate if a2dp link is non-edr */
   if (!a2dp_sbc_encoder_cb.is_peer_edr) {
     rate = A2DP_SBC_NON_EDR_MAX_RATE;
-    LOG_VERBOSE(LOG_TAG, "%s: non-edr a2dp sink detected, restrict rate to %d",
-                __func__, rate);
+    LOG_VERBOSE("%s: non-edr a2dp sink detected, restrict rate to %d", __func__,
+                rate);
   }
 
   return rate;
@@ -846,14 +848,13 @@
   SBC_ENC_PARAMS* p_encoder_params = &a2dp_sbc_encoder_cb.sbc_encoder_params;
   uint32_t frame_len = 0;
 
-  LOG_VERBOSE(LOG_TAG,
-              "%s: channel mode: %d, sub-band: %d, number of block: %d, "
-              "bitpool: %d, sampling frequency: %d, num channels: %d",
-              __func__, p_encoder_params->s16ChannelMode,
-              p_encoder_params->s16NumOfSubBands,
-              p_encoder_params->s16NumOfBlocks, p_encoder_params->s16BitPool,
-              p_encoder_params->s16SamplingFreq,
-              p_encoder_params->s16NumOfChannels);
+  LOG_VERBOSE(
+      "%s: channel mode: %d, sub-band: %d, number of block: %d, "
+      "bitpool: %d, sampling frequency: %d, num channels: %d",
+      __func__, p_encoder_params->s16ChannelMode,
+      p_encoder_params->s16NumOfSubBands, p_encoder_params->s16NumOfBlocks,
+      p_encoder_params->s16BitPool, p_encoder_params->s16SamplingFreq,
+      p_encoder_params->s16NumOfChannels);
 
   switch (p_encoder_params->s16ChannelMode) {
     case SBC_MONO:
@@ -891,18 +892,17 @@
                    CHAR_BIT);
       break;
     default:
-      LOG_VERBOSE(LOG_TAG, "%s: Invalid channel number: %d", __func__,
+      LOG_VERBOSE("%s: Invalid channel number: %d", __func__,
                   p_encoder_params->s16ChannelMode);
       break;
   }
-  LOG_VERBOSE(LOG_TAG, "%s: calculated frame length: %d", __func__, frame_len);
+  LOG_VERBOSE("%s: calculated frame length: %d", __func__, frame_len);
   return frame_len;
 }
 
 uint32_t a2dp_sbc_get_bitrate() {
   SBC_ENC_PARAMS* p_encoder_params = &a2dp_sbc_encoder_cb.sbc_encoder_params;
-  LOG_DEBUG(LOG_TAG, "%s: bit rate %d ", __func__,
-            p_encoder_params->u16BitRate);
+  LOG_DEBUG("%s: bit rate %d ", __func__, p_encoder_params->u16BitRate);
   return p_encoder_params->u16BitRate * 1000;
 }
 
diff --git a/stack/a2dp/a2dp_vendor_aptx.cc b/stack/a2dp/a2dp_vendor_aptx.cc
index e3e844d..d256b76 100644
--- a/stack/a2dp/a2dp_vendor_aptx.cc
+++ b/stack/a2dp/a2dp_vendor_aptx.cc
@@ -208,15 +208,15 @@
   /* parse configuration */
   status = A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: parsing failed %d", __func__, status);
+    LOG_ERROR("%s: parsing failed %d", __func__, status);
     return status;
   }
 
   /* verify that each parameter is in range */
 
-  LOG_VERBOSE(LOG_TAG, "%s: FREQ peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: FREQ peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.sampleRate, p_cap->sampleRate);
-  LOG_VERBOSE(LOG_TAG, "%s: CH_MODE peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: CH_MODE peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.channelMode, p_cap->channelMode);
 
   /* sampling frequency */
@@ -251,14 +251,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoAptx(&aptx_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoAptx(&aptx_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -274,14 +272,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoAptx(&aptx_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoAptx(&aptx_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -302,8 +298,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAptx(&aptx_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -319,8 +314,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAptx(&aptx_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -334,8 +328,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoAptx(&aptx_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -469,7 +462,7 @@
 
   // Load the encoder
   if (!A2DP_VendorLoadEncoderAptx()) {
-    LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
+    LOG_ERROR("%s: cannot load the encoder", __func__);
     return false;
   }
 
@@ -646,8 +639,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoAptx(&peer_info_cie, p_peer_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
 
@@ -730,11 +723,10 @@
     }
   } while (false);
   if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match sample frequency: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, a2dp_aptx_source_caps.sampleRate,
-              peer_info_cie.sampleRate);
+    LOG_ERROR(
+        "%s: cannot match sample frequency: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, a2dp_aptx_source_caps.sampleRate, peer_info_cie.sampleRate);
     goto fail;
   }
 
@@ -786,8 +778,7 @@
     }
   } while (false);
   if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match bits per sample: user preference = 0x%x",
+    LOG_ERROR("%s: cannot match bits per sample: user preference = 0x%x",
               __func__, codec_user_config_.bits_per_sample);
     goto fail;
   }
@@ -858,11 +849,10 @@
     }
   } while (false);
   if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match channel mode: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, a2dp_aptx_source_caps.channelMode,
-              peer_info_cie.channelMode);
+    LOG_ERROR(
+        "%s: cannot match channel mode: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, a2dp_aptx_source_caps.channelMode, peer_info_cie.channelMode);
     goto fail;
   }
 
@@ -939,8 +929,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoAptx(&peer_info_cie, p_peer_codec_capabilities, true);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
 
diff --git a/stack/a2dp/a2dp_vendor_aptx_encoder.cc b/stack/a2dp/a2dp_vendor_aptx_encoder.cc
index 0d605c0..b27e775 100644
--- a/stack/a2dp/a2dp_vendor_aptx_encoder.cc
+++ b/stack/a2dp/a2dp_vendor_aptx_encoder.cc
@@ -119,7 +119,7 @@
   // Open the encoder library
   aptx_encoder_lib_handle = dlopen(APTX_ENCODER_LIB_NAME, RTLD_NOW);
   if (aptx_encoder_lib_handle == NULL) {
-    LOG_ERROR(LOG_TAG, "%s: cannot open aptX encoder library %s: %s", __func__,
+    LOG_ERROR("%s: cannot open aptX encoder library %s: %s", __func__,
               APTX_ENCODER_LIB_NAME, dlerror());
     return false;
   }
@@ -127,8 +127,7 @@
   aptx_encoder_init_func = (tAPTX_ENCODER_INIT)dlsym(aptx_encoder_lib_handle,
                                                      APTX_ENCODER_INIT_NAME);
   if (aptx_encoder_init_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the encoder library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the encoder library: %s",
               __func__, APTX_ENCODER_INIT_NAME, dlerror());
     A2DP_VendorUnloadEncoderAptx();
     return false;
@@ -137,8 +136,7 @@
   aptx_encoder_encode_stereo_func = (tAPTX_ENCODER_ENCODE_STEREO)dlsym(
       aptx_encoder_lib_handle, APTX_ENCODER_ENCODE_STEREO_NAME);
   if (aptx_encoder_encode_stereo_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the encoder library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the encoder library: %s",
               __func__, APTX_ENCODER_ENCODE_STEREO_NAME, dlerror());
     A2DP_VendorUnloadEncoderAptx();
     return false;
@@ -147,8 +145,7 @@
   aptx_encoder_sizeof_params_func = (tAPTX_ENCODER_SIZEOF_PARAMS)dlsym(
       aptx_encoder_lib_handle, APTX_ENCODER_SIZEOF_PARAMS_NAME);
   if (aptx_encoder_sizeof_params_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the encoder library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the encoder library: %s",
               __func__, APTX_ENCODER_SIZEOF_PARAMS_NAME, dlerror());
     A2DP_VendorUnloadEncoderAptx();
     return false;
@@ -196,7 +193,7 @@
   if (a2dp_aptx_encoder_cb.aptx_encoder_state != NULL) {
     aptx_encoder_init_func(a2dp_aptx_encoder_cb.aptx_encoder_state, 0);
   } else {
-    LOG_ERROR(LOG_TAG, "%s: Cannot allocate aptX encoder state", __func__);
+    LOG_ERROR("%s: Cannot allocate aptX encoder state", __func__);
     // TODO: Return an error?
   }
 
@@ -219,10 +216,10 @@
   a2dp_aptx_encoder_cb.timestamp = 0;
 
   if (a2dp_aptx_encoder_cb.peer_mtu == 0) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid peer MTU",
-              __func__, name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid peer MTU",
+        __func__, name().c_str());
     return false;
   }
 
@@ -246,10 +243,10 @@
   *p_restart_output = false;
   *p_config_updated = false;
   if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid codec config",
-              __func__, a2dp_codec_config->name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid codec config",
+        __func__, a2dp_codec_config->name().c_str());
     return;
   }
   const uint8_t* p_codec_info = codec_info;
@@ -262,9 +259,9 @@
       a2dp_codec_config->getAudioBitsPerSample();
   p_feeding_params->channel_count =
       A2DP_VendorGetTrackChannelCountAptx(p_codec_info);
-  LOG_DEBUG(LOG_TAG, "%s: sample_rate=%u bits_per_sample=%u channel_count=%u",
-            __func__, p_feeding_params->sample_rate,
-            p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
+  LOG_DEBUG("%s: sample_rate=%u bits_per_sample=%u channel_count=%u", __func__,
+            p_feeding_params->sample_rate, p_feeding_params->bits_per_sample,
+            p_feeding_params->channel_count);
   a2dp_vendor_aptx_feeding_reset();
 }
 
@@ -299,7 +296,7 @@
     }
   }
 
-  LOG_DEBUG(LOG_TAG, "%s: sleep_time_ns = %" PRIu64, __func__,
+  LOG_DEBUG("%s: sleep_time_ns = %" PRIu64, __func__,
             framing_params->sleep_time_ns);
 }
 
@@ -354,8 +351,7 @@
     }
   }
 
-  LOG_VERBOSE(LOG_TAG,
-              "%s: sleep_time_ns = %" PRIu64
+  LOG_VERBOSE("%s: sleep_time_ns = %" PRIu64
               " aptx_bytes = %u "
               "pcm_bytes_per_read = %u pcm_reads = %u frame_size_counter = %u",
               __func__, framing_params->sleep_time_ns,
@@ -404,14 +400,12 @@
   a2dp_aptx_encoder_cb.stats.media_read_total_expected_read_bytes +=
       expected_read_bytes;
 
-  LOG_VERBOSE(LOG_TAG, "%s: PCM read of size %u", __func__,
-              expected_read_bytes);
+  LOG_VERBOSE("%s: PCM read of size %u", __func__, expected_read_bytes);
   bytes_read = a2dp_aptx_encoder_cb.read_callback((uint8_t*)read_buffer16,
                                                   expected_read_bytes);
   a2dp_aptx_encoder_cb.stats.media_read_total_actual_read_bytes += bytes_read;
   if (bytes_read < expected_read_bytes) {
-    LOG_WARN(LOG_TAG,
-             "%s: underflow at PCM reading: read %u bytes instead of %u",
+    LOG_WARN("%s: underflow at PCM reading: read %u bytes instead of %u",
              __func__, bytes_read, expected_read_bytes);
     a2dp_aptx_encoder_cb.stats.media_read_total_dropped_packets++;
     osi_free(p_buf);
@@ -430,8 +424,8 @@
   const int COMPRESSION_RATIO = 4;
   size_t encoded_bytes = pcm_bytes_encoded / COMPRESSION_RATIO;
   p_buf->len += encoded_bytes;
-  LOG_VERBOSE(LOG_TAG, "%s: encoded %zu PCM bytes to %zu", __func__,
-              pcm_bytes_encoded, encoded_bytes);
+  LOG_VERBOSE("%s: encoded %zu PCM bytes to %zu", __func__, pcm_bytes_encoded,
+              encoded_bytes);
 
   // Update the RTP timestamp
   *((uint32_t*)(p_buf + 1)) = a2dp_aptx_encoder_cb.timestamp;
diff --git a/stack/a2dp/a2dp_vendor_aptx_hd.cc b/stack/a2dp/a2dp_vendor_aptx_hd.cc
index 9e19d5f..0b5916b 100644
--- a/stack/a2dp/a2dp_vendor_aptx_hd.cc
+++ b/stack/a2dp/a2dp_vendor_aptx_hd.cc
@@ -226,15 +226,15 @@
   /* parse configuration */
   status = A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: parsing failed %d", __func__, status);
+    LOG_ERROR("%s: parsing failed %d", __func__, status);
     return status;
   }
 
   /* verify that each parameter is in range */
 
-  LOG_VERBOSE(LOG_TAG, "%s: FREQ peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: FREQ peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.sampleRate, p_cap->sampleRate);
-  LOG_VERBOSE(LOG_TAG, "%s: CH_MODE peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: CH_MODE peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.channelMode, p_cap->channelMode);
 
   /* sampling frequency */
@@ -265,14 +265,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoAptxHd(&aptx_hd_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoAptxHd(&aptx_hd_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -288,14 +286,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoAptxHd(&aptx_hd_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoAptxHd(&aptx_hd_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -317,8 +313,7 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoAptxHd(&aptx_hd_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -335,8 +330,7 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoAptxHd(&aptx_hd_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -351,8 +345,7 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoAptxHd(&aptx_hd_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -487,7 +480,7 @@
 
   // Load the encoder
   if (!A2DP_VendorLoadEncoderAptxHd()) {
-    LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
+    LOG_ERROR("%s: cannot load the encoder", __func__);
     return false;
   }
 
@@ -664,8 +657,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoAptxHd(&peer_info_cie, p_peer_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
 
@@ -748,11 +741,11 @@
     }
   } while (false);
   if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match sample frequency: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, a2dp_aptx_hd_source_caps.sampleRate,
-              peer_info_cie.sampleRate);
+    LOG_ERROR(
+        "%s: cannot match sample frequency: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, a2dp_aptx_hd_source_caps.sampleRate,
+        peer_info_cie.sampleRate);
     goto fail;
   }
 
@@ -804,8 +797,7 @@
     }
   } while (false);
   if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match bits per sample: user preference = 0x%x",
+    LOG_ERROR("%s: cannot match bits per sample: user preference = 0x%x",
               __func__, codec_user_config_.bits_per_sample);
     goto fail;
   }
@@ -877,11 +869,11 @@
     }
   } while (false);
   if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match channel mode: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, a2dp_aptx_hd_source_caps.channelMode,
-              peer_info_cie.channelMode);
+    LOG_ERROR(
+        "%s: cannot match channel mode: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, a2dp_aptx_hd_source_caps.channelMode,
+        peer_info_cie.channelMode);
     goto fail;
   }
 
@@ -965,8 +957,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoAptxHd(&peer_info_cie, p_peer_codec_capabilities, true);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
 
diff --git a/stack/a2dp/a2dp_vendor_aptx_hd_encoder.cc b/stack/a2dp/a2dp_vendor_aptx_hd_encoder.cc
index fb782dd..c9f2ccb 100644
--- a/stack/a2dp/a2dp_vendor_aptx_hd_encoder.cc
+++ b/stack/a2dp/a2dp_vendor_aptx_hd_encoder.cc
@@ -119,16 +119,15 @@
   // Open the encoder library
   aptx_hd_encoder_lib_handle = dlopen(APTX_HD_ENCODER_LIB_NAME, RTLD_NOW);
   if (aptx_hd_encoder_lib_handle == NULL) {
-    LOG_ERROR(LOG_TAG, "%s: cannot open aptX-HD encoder library %s: %s",
-              __func__, APTX_HD_ENCODER_LIB_NAME, dlerror());
+    LOG_ERROR("%s: cannot open aptX-HD encoder library %s: %s", __func__,
+              APTX_HD_ENCODER_LIB_NAME, dlerror());
     return false;
   }
 
   aptx_hd_encoder_init_func = (tAPTX_HD_ENCODER_INIT)dlsym(
       aptx_hd_encoder_lib_handle, APTX_HD_ENCODER_INIT_NAME);
   if (aptx_hd_encoder_init_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the encoder library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the encoder library: %s",
               __func__, APTX_HD_ENCODER_INIT_NAME, dlerror());
     A2DP_VendorUnloadEncoderAptxHd();
     return false;
@@ -137,8 +136,7 @@
   aptx_hd_encoder_encode_stereo_func = (tAPTX_HD_ENCODER_ENCODE_STEREO)dlsym(
       aptx_hd_encoder_lib_handle, APTX_HD_ENCODER_ENCODE_STEREO_NAME);
   if (aptx_hd_encoder_encode_stereo_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the encoder library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the encoder library: %s",
               __func__, APTX_HD_ENCODER_ENCODE_STEREO_NAME, dlerror());
     A2DP_VendorUnloadEncoderAptxHd();
     return false;
@@ -147,8 +145,7 @@
   aptx_hd_encoder_sizeof_params_func = (tAPTX_HD_ENCODER_SIZEOF_PARAMS)dlsym(
       aptx_hd_encoder_lib_handle, APTX_HD_ENCODER_SIZEOF_PARAMS_NAME);
   if (aptx_hd_encoder_sizeof_params_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the encoder library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the encoder library: %s",
               __func__, APTX_HD_ENCODER_SIZEOF_PARAMS_NAME, dlerror());
     A2DP_VendorUnloadEncoderAptxHd();
     return false;
@@ -197,7 +194,7 @@
   if (a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state != NULL) {
     aptx_hd_encoder_init_func(a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state, 0);
   } else {
-    LOG_ERROR(LOG_TAG, "%s: Cannot allocate aptX-HD encoder state", __func__);
+    LOG_ERROR("%s: Cannot allocate aptX-HD encoder state", __func__);
     // TODO: Return an error?
   }
 
@@ -221,10 +218,10 @@
   a2dp_aptx_hd_encoder_cb.timestamp = 0;
 
   if (a2dp_aptx_hd_encoder_cb.peer_mtu == 0) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid peer MTU",
-              __func__, name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid peer MTU",
+        __func__, name().c_str());
     return false;
   }
 
@@ -246,10 +243,10 @@
   *p_restart_output = false;
   *p_config_updated = false;
   if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid codec config",
-              __func__, a2dp_codec_config->name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid codec config",
+        __func__, a2dp_codec_config->name().c_str());
     return;
   }
   const uint8_t* p_codec_info = codec_info;
@@ -263,9 +260,9 @@
       a2dp_codec_config->getAudioBitsPerSample();
   p_feeding_params->channel_count =
       A2DP_VendorGetTrackChannelCountAptxHd(p_codec_info);
-  LOG_DEBUG(LOG_TAG, "%s: sample_rate=%u bits_per_sample=%u channel_count=%u",
-            __func__, p_feeding_params->sample_rate,
-            p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
+  LOG_DEBUG("%s: sample_rate=%u bits_per_sample=%u channel_count=%u", __func__,
+            p_feeding_params->sample_rate, p_feeding_params->bits_per_sample,
+            p_feeding_params->channel_count);
   a2dp_vendor_aptx_hd_feeding_reset();
 }
 
@@ -288,7 +285,7 @@
 
   framing_params->sleep_time_ns = 9000000;
 
-  LOG_DEBUG(LOG_TAG, "%s: sleep_time_ns = %" PRIu64, __func__,
+  LOG_DEBUG("%s: sleep_time_ns = %" PRIu64, __func__,
             framing_params->sleep_time_ns);
 }
 
@@ -338,8 +335,7 @@
       framing_params->frame_size_counter = 0;
   }
 
-  LOG_VERBOSE(LOG_TAG,
-              "%s: sleep_time_ns = %" PRIu64
+  LOG_VERBOSE("%s: sleep_time_ns = %" PRIu64
               " aptx_hd_bytes = %u "
               "pcm_bytes_per_read = %u pcm_reads = %u frame_size_counter = %u",
               __func__, framing_params->sleep_time_ns,
@@ -390,15 +386,13 @@
   a2dp_aptx_hd_encoder_cb.stats.media_read_total_expected_read_bytes +=
       expected_read_bytes;
 
-  LOG_VERBOSE(LOG_TAG, "%s: PCM read of size %u", __func__,
-              expected_read_bytes);
+  LOG_VERBOSE("%s: PCM read of size %u", __func__, expected_read_bytes);
   bytes_read = a2dp_aptx_hd_encoder_cb.read_callback((uint8_t*)read_buffer32,
                                                      expected_read_bytes);
   a2dp_aptx_hd_encoder_cb.stats.media_read_total_actual_read_bytes +=
       bytes_read;
   if (bytes_read < expected_read_bytes) {
-    LOG_WARN(LOG_TAG,
-             "%s: underflow at PCM reading: read %u bytes instead of %u",
+    LOG_WARN("%s: underflow at PCM reading: read %u bytes instead of %u",
              __func__, bytes_read, expected_read_bytes);
     a2dp_aptx_hd_encoder_cb.stats.media_read_total_dropped_packets++;
     osi_free(p_buf);
@@ -418,8 +412,8 @@
   const int COMPRESSION_RATIO = 4;
   size_t encoded_bytes = pcm_bytes_encoded / COMPRESSION_RATIO;
   p_buf->len += encoded_bytes;
-  LOG_VERBOSE(LOG_TAG, "%s: encoded %zu PCM bytes to %zu", __func__,
-              pcm_bytes_encoded, encoded_bytes);
+  LOG_VERBOSE("%s: encoded %zu PCM bytes to %zu", __func__, pcm_bytes_encoded,
+              encoded_bytes);
 
   // Update the RTP timestamp
   *((uint32_t*)(p_buf + 1)) = a2dp_aptx_hd_encoder_cb.timestamp;
diff --git a/stack/a2dp/a2dp_vendor_ldac.cc b/stack/a2dp/a2dp_vendor_ldac.cc
index 4949e74..b9e5f5a 100644
--- a/stack/a2dp/a2dp_vendor_ldac.cc
+++ b/stack/a2dp/a2dp_vendor_ldac.cc
@@ -69,7 +69,8 @@
     (A2DP_LDAC_SAMPLING_FREQ_44100 | A2DP_LDAC_SAMPLING_FREQ_48000 |
      A2DP_LDAC_SAMPLING_FREQ_88200 | A2DP_LDAC_SAMPLING_FREQ_96000),
     // channelMode
-    (A2DP_LDAC_CHANNEL_MODE_DUAL | A2DP_LDAC_CHANNEL_MODE_STEREO),
+    (A2DP_LDAC_CHANNEL_MODE_MONO | A2DP_LDAC_CHANNEL_MODE_DUAL |
+     A2DP_LDAC_CHANNEL_MODE_STEREO),
     // bits_per_sample
     (BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 | BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24 |
      BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32)};
@@ -93,9 +94,9 @@
     a2dp_vendor_ldac_set_transmit_queue_length};
 
 static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_ldac = {
-    a2dp_vendor_ldac_decoder_init,
-    a2dp_vendor_ldac_decoder_cleanup,
-    a2dp_vendor_ldac_decoder_decode_packet,
+    a2dp_vendor_ldac_decoder_init,          a2dp_vendor_ldac_decoder_cleanup,
+    a2dp_vendor_ldac_decoder_decode_packet, a2dp_vendor_ldac_decoder_start,
+    a2dp_vendor_ldac_decoder_suspend,       a2dp_vendor_ldac_decoder_configure,
 };
 
 UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityLdac(
@@ -279,15 +280,15 @@
   /* parse configuration */
   status = A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: parsing failed %d", __func__, status);
+    LOG_ERROR("%s: parsing failed %d", __func__, status);
     return status;
   }
 
   /* verify that each parameter is in range */
 
-  LOG_VERBOSE(LOG_TAG, "%s: FREQ peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: FREQ peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.sampleRate, p_cap->sampleRate);
-  LOG_VERBOSE(LOG_TAG, "%s: CH_MODE peer: 0x%x, capability 0x%x", __func__,
+  LOG_VERBOSE("%s: CH_MODE peer: 0x%x, capability 0x%x", __func__,
               cfg_cie.channelMode, p_cap->channelMode);
 
   /* sampling frequency */
@@ -318,14 +319,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoLdac(&ldac_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoLdac(&ldac_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -341,14 +340,12 @@
   tA2DP_STATUS a2dp_status =
       A2DP_ParseInfoLdac(&ldac_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
   a2dp_status = A2DP_ParseInfoLdac(&ldac_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return false;
   }
 
@@ -392,8 +389,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoLdac(&ldac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -421,8 +417,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoLdac(&ldac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -450,8 +445,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoLdac(&ldac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -473,8 +467,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoLdac(&ldac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -496,8 +489,7 @@
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status = A2DP_ParseInfoLdac(&ldac_cie, p_codec_info, false);
   if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
+    LOG_ERROR("%s: cannot decode codec information: %d", __func__, a2dp_status);
     return -1;
   }
 
@@ -711,7 +703,7 @@
 
   // Load the encoder
   if (!A2DP_VendorLoadEncoderLdac()) {
-    LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
+    LOG_ERROR("%s: cannot load the encoder", __func__);
     return false;
   }
 
@@ -979,8 +971,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoLdac(&peer_info_cie, p_peer_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
 
@@ -1111,10 +1103,10 @@
     }
   } while (false);
   if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match sample frequency: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, p_a2dp_ldac_caps->sampleRate, peer_info_cie.sampleRate);
+    LOG_ERROR(
+        "%s: cannot match sample frequency: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, p_a2dp_ldac_caps->sampleRate, peer_info_cie.sampleRate);
     goto fail;
   }
 
@@ -1186,11 +1178,11 @@
     }
   } while (false);
   if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match bits per sample: default = 0x%x "
-              "user preference = 0x%x",
-              __func__, a2dp_ldac_default_config.bits_per_sample,
-              codec_user_config_.bits_per_sample);
+    LOG_ERROR(
+        "%s: cannot match bits per sample: default = 0x%x "
+        "user preference = 0x%x",
+        __func__, a2dp_ldac_default_config.bits_per_sample,
+        codec_user_config_.bits_per_sample);
     goto fail;
   }
 
@@ -1273,11 +1265,10 @@
     }
   } while (false);
   if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match channel mode: local caps = 0x%x "
-              "peer info = 0x%x",
-              __func__, p_a2dp_ldac_caps->channelMode,
-              peer_info_cie.channelMode);
+    LOG_ERROR(
+        "%s: cannot match channel mode: local caps = 0x%x "
+        "peer info = 0x%x",
+        __func__, p_a2dp_ldac_caps->channelMode, peer_info_cie.channelMode);
     goto fail;
   }
 
@@ -1347,8 +1338,8 @@
   tA2DP_STATUS status =
       A2DP_ParseInfoLdac(&peer_info_cie, p_peer_codec_capabilities, true);
   if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's capabilities: error = %d",
-              __func__, status);
+    LOG_ERROR("%s: can't parse peer's capabilities: error = %d", __func__,
+              status);
     goto fail;
   }
 
@@ -1424,7 +1415,7 @@
 
   // Load the decoder
   if (!A2DP_VendorLoadDecoderLdac()) {
-    LOG_ERROR(LOG_TAG, "%s: cannot load the decoder", __func__);
+    LOG_ERROR("%s: cannot load the decoder", __func__);
     return false;
   }
 
diff --git a/stack/a2dp/a2dp_vendor_ldac_abr.cc b/stack/a2dp/a2dp_vendor_ldac_abr.cc
index 7715af4..635c6ee 100644
--- a/stack/a2dp/a2dp_vendor_ldac_abr.cc
+++ b/stack/a2dp/a2dp_vendor_ldac_abr.cc
@@ -68,7 +68,7 @@
   // Open the LDAC ABR library
   ldac_abr_lib_handle = dlopen(LDAC_ABR_LIB_NAME, RTLD_NOW);
   if (ldac_abr_lib_handle == NULL) {
-    LOG_ERROR(LOG_TAG, "%s: cannot open LDAC ABR library %s: %s", __func__,
+    LOG_ERROR("%s: cannot open LDAC ABR library %s: %s", __func__,
               LDAC_ABR_LIB_NAME, dlerror());
     A2DP_VendorUnloadLdacAbr();
     return false;
@@ -78,8 +78,7 @@
   ldac_abr_get_handle_func = (tLDAC_ABR_GET_HANDLE)dlsym(
       ldac_abr_lib_handle, LDAC_ABR_GET_HANDLE_NAME);
   if (ldac_abr_get_handle_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the LDAC ABR library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the LDAC ABR library: %s",
               __func__, LDAC_ABR_GET_HANDLE_NAME, dlerror());
     A2DP_VendorUnloadLdacAbr();
     return false;
@@ -88,8 +87,7 @@
   ldac_abr_free_handle_func = (tLDAC_ABR_FREE_HANDLE)dlsym(
       ldac_abr_lib_handle, LDAC_ABR_FREE_HANDLE_NAME);
   if (ldac_abr_free_handle_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the LDAC ABR library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the LDAC ABR library: %s",
               __func__, LDAC_ABR_FREE_HANDLE_NAME, dlerror());
     A2DP_VendorUnloadLdacAbr();
     return false;
@@ -98,8 +96,7 @@
   ldac_abr_init_func =
       (tLDAC_ABR_INIT)dlsym(ldac_abr_lib_handle, LDAC_ABR_INIT_NAME);
   if (ldac_abr_init_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the LDAC ABR library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the LDAC ABR library: %s",
               __func__, LDAC_ABR_INIT_NAME, dlerror());
     A2DP_VendorUnloadLdacAbr();
     return false;
@@ -108,8 +105,7 @@
   ldac_abr_set_thresholds_func = (tLDAC_ABR_SET_THRESHOLDS)dlsym(
       ldac_abr_lib_handle, LDAC_ABR_SET_THRESHOLDS_NAME);
   if (ldac_abr_set_thresholds_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the LDAC ABR library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the LDAC ABR library: %s",
               __func__, LDAC_ABR_SET_THRESHOLDS_NAME, dlerror());
     A2DP_VendorUnloadLdacAbr();
     return false;
@@ -118,8 +114,7 @@
   ldac_abr_proc_func =
       (tLDAC_ABR_PROC)dlsym(ldac_abr_lib_handle, LDAC_ABR_PROC_NAME);
   if (ldac_abr_proc_func == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the LDAC ABR library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the LDAC ABR library: %s",
               __func__, LDAC_ABR_PROC_NAME, dlerror());
     A2DP_VendorUnloadLdacAbr();
     return false;
diff --git a/stack/a2dp/a2dp_vendor_ldac_decoder.cc b/stack/a2dp/a2dp_vendor_ldac_decoder.cc
index 444af34..fe54bb2 100644
--- a/stack/a2dp/a2dp_vendor_ldac_decoder.cc
+++ b/stack/a2dp/a2dp_vendor_ldac_decoder.cc
@@ -24,10 +24,12 @@
 #endif
 #include <dlfcn.h>
 #include <inttypes.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <string.h>
 
 #include <ldacBT.h>
+#include <ldacBT_bco_for_fluoride.h>
 
 #include "a2dp_vendor.h"
 #include "a2dp_vendor_ldac.h"
@@ -40,50 +42,40 @@
 //
 
 //
-// The LDAC decoder shared library, and the functions to use
+// The LDAC BCO shared library, and the functions to use
 //
-static const char* LDAC_DECODER_LIB_NAME = "libldacBT_dec.so";
-static void* ldac_decoder_lib_handle = NULL;
+static const char* LDAC_BCO_LIB_NAME = "libldacBT_bco.so";
+static void* ldac_bco_lib_handle = NULL;
 
-static const char* LDAC_GET_HANDLE_NAME = "ldacBT_get_handle";
-typedef HANDLE_LDAC_BT (*tLDAC_GET_HANDLE)(void);
+static const char* LDAC_BCO_INIT_NAME = "ldac_BCO_init";
+typedef HANDLE_LDAC_BCO (*tLDAC_BCO_INIT)(
+    decoded_data_callback_t decode_callback);
 
-static const char* LDAC_FREE_HANDLE_NAME = "ldacBT_free_handle";
-typedef void (*tLDAC_FREE_HANDLE)(HANDLE_LDAC_BT hLdacParam);
+static const char* LDAC_BCO_CLEANUP_NAME = "ldac_BCO_cleanup";
+typedef int32_t (*tLDAC_BCO_CLEANUP)(HANDLE_LDAC_BCO hLdacBco);
 
-static const char* LDAC_CLOSE_HANDLE_NAME = "ldacBT_close_handle";
-typedef void (*tLDAC_CLOSE_HANDLE)(HANDLE_LDAC_BT hLdacParam);
+static const char* LDAC_BCO_DECODE_PACKET_NAME = "ldac_BCO_decode_packet";
+typedef int32_t (*tLDAC_BCO_DECODE_PACKET)(HANDLE_LDAC_BCO hLdacBco, void* data,
+                                           int32_t length);
 
-static const char* LDAC_GET_VERSION_NAME = "ldacBT_get_version";
-typedef int (*tLDAC_GET_VERSION)(void);
+static const char* LDAC_BCO_START_NAME = "ldac_BCO_start";
+typedef int32_t (*tLDAC_BCO_START)(HANDLE_LDAC_BCO hLdacBco);
 
-static const char* LDAC_GET_BITRATE_NAME = "ldacBT_get_bitrate";
-typedef int (*tLDAC_GET_BITRATE)(HANDLE_LDAC_BT hLdacParam);
+static const char* LDAC_BCO_SUSPEND_NAME = "ldac_BCO_suspend";
+typedef int32_t (*tLDAC_BCO_SUSPEND)(HANDLE_LDAC_BCO hLdacBco);
 
-static const char* LDAC_GET_SAMPLING_FREQ_NAME = "ldacBT_get_sampling_freq";
-typedef int (*tLDAC_GET_SAMPLING_FREQ)(HANDLE_LDAC_BT hLdacParam);
+static const char* LDAC_BCO_CONFIGURE_NAME = "ldac_BCO_configure";
+typedef int32_t (*tLDAC_BCO_CONFIGURE)(HANDLE_LDAC_BCO hLdacBco,
+                                       int32_t sample_rate,
+                                       int32_t bits_per_sample,
+                                       int32_t channel_mode);
 
-static const char* LDAC_INIT_HANDLE_DECODE_NAME = "ldacBT_init_handle_decode";
-typedef int (*tLDAC_INIT_HANDLE_DECODE)(HANDLE_LDAC_BT hLdacParam, int cm,
-                                        int sf, int var0, int var1, int var2);
-
-static const char* LDAC_DECODE_NAME = "ldacBT_decode";
-typedef int (*tLDAC_DECODE)(HANDLE_LDAC_BT hLdacBt, unsigned char* p_bs,
-                            unsigned char* p_pcm, LDACBT_SMPL_FMT_T fmt,
-                            int bs_bytes, int* used_bytes, int* wrote_bytes);
-
-static const char* LDAC_GET_ERROR_CODE_NAME = "ldacBT_get_error_code";
-typedef int (*tLDAC_GET_ERROR_CODE)(HANDLE_LDAC_BT hLdacParam);
-
-static tLDAC_GET_HANDLE ldac_get_handle_func;
-static tLDAC_FREE_HANDLE ldac_free_handle_func;
-static tLDAC_CLOSE_HANDLE ldac_close_handle_func;
-static tLDAC_GET_VERSION ldac_get_version_func;
-static tLDAC_GET_BITRATE ldac_get_bitrate_func;
-static tLDAC_GET_SAMPLING_FREQ ldac_get_sampling_freq_func;
-static tLDAC_INIT_HANDLE_DECODE ldac_init_handle_decode_func;
-static tLDAC_DECODE ldac_decode_func;
-static tLDAC_GET_ERROR_CODE ldac_get_error_code_func;
+static tLDAC_BCO_INIT ldac_BCO_init_func;
+static tLDAC_BCO_CLEANUP ldac_BCO_cleanup_func;
+static tLDAC_BCO_DECODE_PACKET ldac_BCO_decode_packet_func;
+static tLDAC_BCO_START ldac_BCO_start_func;
+static tLDAC_BCO_SUSPEND ldac_BCO_suspend_func;
+static tLDAC_BCO_CONFIGURE ldac_BCO_configure_func;
 
 // offset
 #if (BTA_AV_CO_CP_SCMS_T == TRUE)
@@ -101,13 +93,14 @@
 } tA2DP_LDAC_DECODER_PARAMS;
 
 typedef struct {
+  pthread_mutex_t mutex;
   bool use_SCMS_T;
   bool is_peer_edr;          // True if the peer device supports EDR
   bool peer_supports_3mbps;  // True if the peer device supports 3Mbps EDR
   uint16_t peer_mtu;         // MTU of the A2DP peer
   uint32_t timestamp;        // Timestamp for the A2DP frames
 
-  HANDLE_LDAC_BT ldac_handle;
+  HANDLE_LDAC_BCO ldac_handle_bco;
   bool has_ldac_handle;  // True if ldac_handle is valid
   unsigned char* decode_buf;
   decoded_data_callback_t decode_callback;
@@ -116,10 +109,9 @@
 static tA2DP_LDAC_DECODER_CB a2dp_ldac_decoder_cb;
 
 static void* load_func(const char* func_name) {
-  void* func_ptr = dlsym(ldac_decoder_lib_handle, func_name);
+  void* func_ptr = dlsym(ldac_bco_lib_handle, func_name);
   if (func_ptr == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the decoder library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the decoder library: %s",
               __func__, func_name, dlerror());
     A2DP_VendorUnloadDecoderLdac();
     return NULL;
@@ -128,154 +120,155 @@
 }
 
 bool A2DP_VendorLoadDecoderLdac(void) {
-  if (ldac_decoder_lib_handle != NULL) return true;  // Already loaded
+  if (ldac_bco_lib_handle != NULL) return true;  // Already loaded
 
   // Initialize the control block
   memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
 
+  pthread_mutex_init(&(a2dp_ldac_decoder_cb.mutex), NULL);
+
   // Open the decoder library
-  ldac_decoder_lib_handle = dlopen(LDAC_DECODER_LIB_NAME, RTLD_NOW);
-  if (ldac_decoder_lib_handle == NULL) {
-    LOG_ERROR(LOG_TAG, "%s: cannot open LDAC decoder library %s: %s", __func__,
-              LDAC_DECODER_LIB_NAME, dlerror());
+  ldac_bco_lib_handle = dlopen(LDAC_BCO_LIB_NAME, RTLD_NOW);
+  if (ldac_bco_lib_handle == NULL) {
+    LOG_ERROR("%s: cannot open LDAC decoder library %s: %s", __func__,
+              LDAC_BCO_LIB_NAME, dlerror());
     return false;
   }
 
   // Load all functions
-  ldac_get_handle_func = (tLDAC_GET_HANDLE)load_func(LDAC_GET_HANDLE_NAME);
-  if (ldac_get_handle_func == NULL) return false;
-  ldac_free_handle_func = (tLDAC_FREE_HANDLE)load_func(LDAC_FREE_HANDLE_NAME);
-  if (ldac_free_handle_func == NULL) return false;
-  ldac_close_handle_func =
-      (tLDAC_CLOSE_HANDLE)load_func(LDAC_CLOSE_HANDLE_NAME);
-  if (ldac_close_handle_func == NULL) return false;
-  ldac_get_version_func = (tLDAC_GET_VERSION)load_func(LDAC_GET_VERSION_NAME);
-  if (ldac_get_version_func == NULL) return false;
-  ldac_get_bitrate_func = (tLDAC_GET_BITRATE)load_func(LDAC_GET_BITRATE_NAME);
-  if (ldac_get_bitrate_func == NULL) return false;
-  ldac_get_sampling_freq_func =
-      (tLDAC_GET_SAMPLING_FREQ)load_func(LDAC_GET_SAMPLING_FREQ_NAME);
-  if (ldac_get_sampling_freq_func == NULL) return false;
-  ldac_init_handle_decode_func =
-      (tLDAC_INIT_HANDLE_DECODE)load_func(LDAC_INIT_HANDLE_DECODE_NAME);
-  if (ldac_init_handle_decode_func == NULL) return false;
-  ldac_decode_func = (tLDAC_DECODE)load_func(LDAC_DECODE_NAME);
-  if (ldac_decode_func == NULL) return false;
-  ldac_get_error_code_func =
-      (tLDAC_GET_ERROR_CODE)load_func(LDAC_GET_ERROR_CODE_NAME);
-  if (ldac_get_error_code_func == NULL) return false;
+  ldac_BCO_init_func = (tLDAC_BCO_INIT)load_func(LDAC_BCO_INIT_NAME);
+  if (ldac_BCO_init_func == NULL) return false;
+
+  ldac_BCO_cleanup_func = (tLDAC_BCO_CLEANUP)load_func(LDAC_BCO_CLEANUP_NAME);
+  if (ldac_BCO_cleanup_func == NULL) return false;
+
+  ldac_BCO_decode_packet_func =
+      (tLDAC_BCO_DECODE_PACKET)load_func(LDAC_BCO_DECODE_PACKET_NAME);
+  if (ldac_BCO_decode_packet_func == NULL) return false;
+
+  ldac_BCO_start_func = (tLDAC_BCO_START)load_func(LDAC_BCO_START_NAME);
+  if (ldac_BCO_start_func == NULL) return false;
+
+  ldac_BCO_suspend_func = (tLDAC_BCO_SUSPEND)load_func(LDAC_BCO_SUSPEND_NAME);
+  if (ldac_BCO_suspend_func == NULL) return false;
+
+  ldac_BCO_configure_func =
+      (tLDAC_BCO_CONFIGURE)load_func(LDAC_BCO_CONFIGURE_NAME);
+  if (ldac_BCO_configure_func == NULL) return false;
 
   return true;
 }
 
 void A2DP_VendorUnloadDecoderLdac(void) {
   // Cleanup any LDAC-related state
-  if (a2dp_ldac_decoder_cb.has_ldac_handle && ldac_free_handle_func != NULL)
-    ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
+  if (a2dp_ldac_decoder_cb.has_ldac_handle && ldac_BCO_cleanup_func != NULL)
+    ldac_BCO_cleanup_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
+  pthread_mutex_destroy(&(a2dp_ldac_decoder_cb.mutex));
   memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
 
-  ldac_get_handle_func = NULL;
-  ldac_free_handle_func = NULL;
-  ldac_close_handle_func = NULL;
-  ldac_get_version_func = NULL;
-  ldac_get_bitrate_func = NULL;
-  ldac_get_sampling_freq_func = NULL;
-  ldac_init_handle_decode_func = NULL;
-  ldac_decode_func = NULL;
-  ldac_get_error_code_func = NULL;
+  ldac_BCO_init_func = NULL;
+  ldac_BCO_cleanup_func = NULL;
+  ldac_BCO_decode_packet_func = NULL;
+  ldac_BCO_start_func = NULL;
+  ldac_BCO_suspend_func = NULL;
+  ldac_BCO_configure_func = NULL;
 
-  if (ldac_decoder_lib_handle != NULL) {
-    dlclose(ldac_decoder_lib_handle);
-    ldac_decoder_lib_handle = NULL;
+  if (ldac_bco_lib_handle != NULL) {
+    dlclose(ldac_bco_lib_handle);
+    ldac_bco_lib_handle = NULL;
   }
 }
 
 bool a2dp_vendor_ldac_decoder_init(decoded_data_callback_t decode_callback) {
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+
   if (a2dp_ldac_decoder_cb.has_ldac_handle)
-    ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
-  memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
+    ldac_BCO_cleanup_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
 
-  a2dp_vendor_ldac_decoder_cleanup();
+  a2dp_ldac_decoder_cb.ldac_handle_bco = ldac_BCO_init_func(decode_callback);
+  a2dp_ldac_decoder_cb.has_ldac_handle =
+      (a2dp_ldac_decoder_cb.ldac_handle_bco != NULL);
 
-  a2dp_ldac_decoder_cb.ldac_handle = ldac_get_handle_func();
-  a2dp_ldac_decoder_cb.has_ldac_handle = true;
-  a2dp_ldac_decoder_cb.decode_buf = static_cast<unsigned char*>(
-      osi_malloc(sizeof(a2dp_ldac_decoder_cb.decode_buf[0]) * LDACBT_MAX_LSU *
-                 LDAC_PRCNCH * sizeof(int)));
-  a2dp_ldac_decoder_cb.decode_callback = decode_callback;
-
-  // initialize
-  ldac_init_handle_decode_func(a2dp_ldac_decoder_cb.ldac_handle,
-                               LDACBT_CHANNEL_MODE_STEREO, 96000, 0, 0, 0);
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
   return true;
 }
 
 void a2dp_vendor_ldac_decoder_cleanup(void) {
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
   if (a2dp_ldac_decoder_cb.has_ldac_handle)
-    ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
-  memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
+    ldac_BCO_cleanup_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
+  a2dp_ldac_decoder_cb.ldac_handle_bco = NULL;
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
 }
 
 bool a2dp_vendor_ldac_decoder_decode_packet(BT_HDR* p_buf) {
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+  if (p_buf == nullptr) {
+    LOG_ERROR("%s Dropping packet with nullptr", __func__);
+    return false;
+  }
   unsigned char* pBuffer =
       reinterpret_cast<unsigned char*>(p_buf->data + p_buf->offset);
   //  unsigned int bufferSize = p_buf->len;
   unsigned int bytesValid = p_buf->len;
-  int err;
+  if (bytesValid == 0) {
+    LOG_WARN("%s Dropping packet with zero length", __func__);
+    return false;
+  }
+
   LDACBT_SMPL_FMT_T fmt;
-  int bs_bytes, used_bytes, wrote_bytes, frame_number;
+  int bs_bytes, frame_number;
 
   fmt = LDACBT_SMPL_FMT_S32;
   frame_number = (int)pBuffer[0];
-  pBuffer++;
-  bs_bytes = (int)bytesValid - 1;
+  bs_bytes = (int)bytesValid;
   bytesValid -= 1;
-  LOG_VERBOSE(LOG_TAG, "%s:INPUT size : %d, frame : %d", __func__, bs_bytes,
-              frame_number);
+  LOG_DEBUG("%s:INPUT size : %d, frame : %d", __func__, bs_bytes, frame_number);
 
-  while (bytesValid > 0) {
-#if 0
-    err = ldacDecoder_Fill(a2dp_ldac_decoder_cb.ldac_handle,
-                               &pBuffer, &bufferSize, &bytesValid);
-    if (err != LDACBT_ERR_NONE) {
-      LOG_ERROR(LOG_TAG, "%s: ldacDecoder_Fill failed: 0x%x", __func__,
-                static_cast<unsigned>(err));
-      return false;
-    }
-#endif
-    while (true) {
-      // Todo : implement LDAC Buffer Control Operation instead of
-      // ldac_decode_func().
-      err = ldac_decode_func(a2dp_ldac_decoder_cb.ldac_handle, pBuffer,
-                             a2dp_ldac_decoder_cb.decode_buf, fmt, bs_bytes,
-                             &used_bytes, &wrote_bytes);
-      //      if (err == LDAC_DEC_NOT_ENOUGH_FRAMES) {
-      //        break;
-      //      }
-      if (LDACBT_ERROR(err)) {
-        err = ldac_get_error_code_func(a2dp_ldac_decoder_cb.ldac_handle);
-        LOG_ERROR(LOG_TAG, "%s: ldacDecoder_DecodeFrame failed: %d:%d:%d",
-                  __func__, LDACBT_API_ERR(err), LDACBT_HANDLE_ERR(err),
-                  LDACBT_BLOCK_ERR(err));
-        if (LDACBT_FATAL(err)) {
-          break;
-        }
-      }
+  if (a2dp_ldac_decoder_cb.has_ldac_handle)
+    ldac_BCO_decode_packet_func(a2dp_ldac_decoder_cb.ldac_handle_bco, pBuffer,
+                                bs_bytes);
 
-      if (wrote_bytes > 0) {
-        size_t frame_len = (size_t)wrote_bytes;
-        a2dp_ldac_decoder_cb.decode_callback(
-            reinterpret_cast<uint8_t*>(a2dp_ldac_decoder_cb.decode_buf),
-            frame_len);
-      }
-      pBuffer += used_bytes;
-      bs_bytes -= used_bytes;
-      if (bs_bytes <= 1) {
-        bytesValid = 0;
-        break;
-      }
-    }
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
+  return true;
+}
+
+void a2dp_vendor_ldac_decoder_start(void) {
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+  LOG_DEBUG("%s", __func__);
+  if (a2dp_ldac_decoder_cb.has_ldac_handle)
+    ldac_BCO_start_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
+}
+
+void a2dp_vendor_ldac_decoder_suspend(void) {
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+  LOG_DEBUG("%s", __func__);
+  if (a2dp_ldac_decoder_cb.has_ldac_handle)
+    ldac_BCO_suspend_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
+}
+
+void a2dp_vendor_ldac_decoder_configure(const uint8_t* p_codec_info) {
+  int32_t sample_rate;
+  int32_t bits_per_sample;
+  int32_t channel_mode;
+
+  if (p_codec_info == NULL) {
+    LOG_ERROR("%s: p_codec_info is NULL", __func__);
+    return;
   }
 
-  return true;
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+  sample_rate = A2DP_VendorGetTrackSampleRateLdac(p_codec_info);
+  bits_per_sample = A2DP_VendorGetTrackBitsPerSampleLdac(p_codec_info);
+  channel_mode = A2DP_VendorGetChannelModeCodeLdac(p_codec_info);
+
+  LOG_DEBUG("%s , sample_rate=%d, bits_per_sample=%d, channel_mode=%d",
+            __func__, sample_rate, bits_per_sample, channel_mode);
+
+  if (a2dp_ldac_decoder_cb.has_ldac_handle)
+    ldac_BCO_configure_func(a2dp_ldac_decoder_cb.ldac_handle_bco, sample_rate,
+                            bits_per_sample, channel_mode);
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
 }
diff --git a/stack/a2dp/a2dp_vendor_ldac_encoder.cc b/stack/a2dp/a2dp_vendor_ldac_encoder.cc
index ca6c2fc..8da55bf 100644
--- a/stack/a2dp/a2dp_vendor_ldac_encoder.cc
+++ b/stack/a2dp/a2dp_vendor_ldac_encoder.cc
@@ -187,8 +187,7 @@
 static void* load_func(const char* func_name) {
   void* func_ptr = dlsym(ldac_encoder_lib_handle, func_name);
   if (func_ptr == NULL) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot find function '%s' in the encoder library: %s",
+    LOG_ERROR("%s: cannot find function '%s' in the encoder library: %s",
               __func__, func_name, dlerror());
     A2DP_VendorUnloadEncoderLdac();
     return NULL;
@@ -205,7 +204,7 @@
   // Open the encoder library
   ldac_encoder_lib_handle = dlopen(LDAC_ENCODER_LIB_NAME, RTLD_NOW);
   if (ldac_encoder_lib_handle == NULL) {
-    LOG_ERROR(LOG_TAG, "%s: cannot open LDAC encoder library %s: %s", __func__,
+    LOG_ERROR("%s: cannot open LDAC encoder library %s: %s", __func__,
               LDAC_ENCODER_LIB_NAME, dlerror());
     return false;
   }
@@ -242,7 +241,7 @@
   if (ldac_get_error_code_func == NULL) return false;
 
   if (!A2DP_VendorLoadLdacAbr()) {
-    LOG_WARN(LOG_TAG, "%s: cannot load the LDAC ABR library", __func__);
+    LOG_WARN("%s: cannot load the LDAC ABR library", __func__);
     ldac_abr_loaded = false;
   } else {
     ldac_abr_loaded = true;
@@ -324,10 +323,10 @@
   a2dp_ldac_encoder_cb.timestamp = 0;
 
   if (a2dp_ldac_encoder_cb.peer_mtu == 0) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid peer MTU",
-              __func__, name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid peer MTU",
+        __func__, name().c_str());
     return false;
   }
 
@@ -356,7 +355,7 @@
   if (!a2dp_ldac_encoder_cb.has_ldac_handle) {
     a2dp_ldac_encoder_cb.ldac_handle = ldac_get_handle_func();
     if (a2dp_ldac_encoder_cb.ldac_handle == NULL) {
-      LOG_ERROR(LOG_TAG, "%s: Cannot get LDAC encoder handle", __func__);
+      LOG_ERROR("%s: Cannot get LDAC encoder handle", __func__);
       return;  // TODO: Return an error?
     }
     a2dp_ldac_encoder_cb.has_ldac_handle = true;
@@ -364,10 +363,10 @@
   CHECK(a2dp_ldac_encoder_cb.ldac_handle != nullptr);
 
   if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
-    LOG_ERROR(LOG_TAG,
-              "%s: Cannot update the codec encoder for %s: "
-              "invalid codec config",
-              __func__, a2dp_codec_config->name().c_str());
+    LOG_ERROR(
+        "%s: Cannot update the codec encoder for %s: "
+        "invalid codec config",
+        __func__, a2dp_codec_config->name().c_str());
     return;
   }
   const uint8_t* p_codec_info = codec_info;
@@ -381,9 +380,9 @@
       a2dp_codec_config->getAudioBitsPerSample();
   p_feeding_params->channel_count =
       A2DP_VendorGetTrackChannelCountLdac(p_codec_info);
-  LOG_DEBUG(LOG_TAG, "%s: sample_rate=%u bits_per_sample=%u channel_count=%u",
-            __func__, p_feeding_params->sample_rate,
-            p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
+  LOG_DEBUG("%s: sample_rate=%u bits_per_sample=%u channel_count=%u", __func__,
+            p_feeding_params->sample_rate, p_feeding_params->bits_per_sample,
+            p_feeding_params->channel_count);
   a2dp_vendor_ldac_feeding_reset();
 
   // The codec parameters
@@ -404,12 +403,12 @@
   int old_quality_mode_index = p_encoder_params->quality_mode_index;
   if (codec_config.codec_specific_1 != 0) {
     p_encoder_params->quality_mode_index = codec_config.codec_specific_1 % 10;
-    LOG_DEBUG(LOG_TAG, "%s: setting quality mode to %s", __func__,
+    LOG_DEBUG("%s: setting quality mode to %s", __func__,
               quality_mode_index_to_name(p_encoder_params->quality_mode_index)
                   .c_str());
   } else {
     p_encoder_params->quality_mode_index = A2DP_LDAC_QUALITY_ABR;
-    LOG_DEBUG(LOG_TAG, "%s: setting quality mode to default %s", __func__,
+    LOG_DEBUG("%s: setting quality mode to default %s", __func__,
               quality_mode_index_to_name(p_encoder_params->quality_mode_index)
                   .c_str());
   }
@@ -419,21 +418,20 @@
     if (!ldac_abr_loaded) {
       p_encoder_params->quality_mode_index = A2DP_LDAC_QUALITY_MID;
       LOG_WARN(
-          LOG_TAG,
+
           "%s: LDAC ABR library is not loaded, resetting quality mode to %s",
           __func__,
           quality_mode_index_to_name(p_encoder_params->quality_mode_index)
               .c_str());
     } else {
-      LOG_DEBUG(LOG_TAG, "%s: changing mode from %s to %s", __func__,
+      LOG_DEBUG("%s: changing mode from %s to %s", __func__,
                 quality_mode_index_to_name(old_quality_mode_index).c_str(),
                 quality_mode_index_to_name(p_encoder_params->quality_mode_index)
                     .c_str());
       if (a2dp_ldac_encoder_cb.ldac_abr_handle != NULL) {
-        LOG_DEBUG(LOG_TAG, "%s: already in LDAC ABR mode, do nothing.",
-                  __func__);
+        LOG_DEBUG("%s: already in LDAC ABR mode, do nothing.", __func__);
       } else {
-        LOG_DEBUG(LOG_TAG, "%s: get and init LDAC ABR handle.", __func__);
+        LOG_DEBUG("%s: get and init LDAC ABR handle.", __func__);
         a2dp_ldac_encoder_cb.ldac_abr_handle = a2dp_ldac_abr_get_handle();
         if (a2dp_ldac_encoder_cb.ldac_abr_handle != NULL) {
           a2dp_ldac_encoder_cb.has_ldac_abr_handle = true;
@@ -444,7 +442,7 @@
         } else {
           p_encoder_params->quality_mode_index = A2DP_LDAC_QUALITY_MID;
           LOG_DEBUG(
-              LOG_TAG,
+
               "%s: get LDAC ABR handle failed, resetting quality mode to %s.",
               __func__,
               quality_mode_index_to_name(p_encoder_params->quality_mode_index)
@@ -454,7 +452,7 @@
     }
   } else {
     ldac_eqmid = p_encoder_params->quality_mode_index;
-    LOG_DEBUG(LOG_TAG, "%s: in %s mode, free LDAC ABR handle.", __func__,
+    LOG_DEBUG("%s: in %s mode, free LDAC ABR handle.", __func__,
               quality_mode_index_to_name(ldac_eqmid).c_str());
     if (a2dp_ldac_encoder_cb.has_ldac_abr_handle) {
       a2dp_ldac_abr_free_handle(a2dp_ldac_encoder_cb.ldac_abr_handle);
@@ -479,15 +477,14 @@
   else if (p_encoder_params->pcm_wlength == 4)
     p_encoder_params->pcm_fmt = LDACBT_SMPL_FMT_S32;
 
-  LOG_DEBUG(LOG_TAG, "%s: MTU=%d, peer_mtu=%d", __func__,
+  LOG_DEBUG("%s: MTU=%d, peer_mtu=%d", __func__,
             a2dp_ldac_encoder_cb.TxAaMtuSize, peer_mtu);
-  LOG_DEBUG(LOG_TAG,
-            "%s: sample_rate: %d channel_mode: %d "
-            "quality_mode_index: %d pcm_wlength: %d pcm_fmt: %d",
-            __func__, p_encoder_params->sample_rate,
-            p_encoder_params->channel_mode,
-            p_encoder_params->quality_mode_index, p_encoder_params->pcm_wlength,
-            p_encoder_params->pcm_fmt);
+  LOG_DEBUG(
+      "%s: sample_rate: %d channel_mode: %d "
+      "quality_mode_index: %d pcm_wlength: %d pcm_fmt: %d",
+      __func__, p_encoder_params->sample_rate, p_encoder_params->channel_mode,
+      p_encoder_params->quality_mode_index, p_encoder_params->pcm_wlength,
+      p_encoder_params->pcm_fmt);
 
   // Initialize the encoder.
   // NOTE: MTU in the initialization must include the AVDT media header size.
@@ -498,12 +495,11 @@
       p_encoder_params->sample_rate);
   if (result != 0) {
     int err_code = ldac_get_error_code_func(a2dp_ldac_encoder_cb.ldac_handle);
-    LOG_ERROR(LOG_TAG,
-              "%s: error initializing the LDAC encoder: %d api_error = %d "
-              "handle_error = %d block_error = %d error_code = 0x%x",
-              __func__, result, LDACBT_API_ERR(err_code),
-              LDACBT_HANDLE_ERR(err_code), LDACBT_BLOCK_ERR(err_code),
-              err_code);
+    LOG_ERROR(
+        "%s: error initializing the LDAC encoder: %d api_error = %d "
+        "handle_error = %d block_error = %d error_code = 0x%x",
+        __func__, result, LDACBT_API_ERR(err_code), LDACBT_HANDLE_ERR(err_code),
+        LDACBT_BLOCK_ERR(err_code), err_code);
   }
 }
 
@@ -527,7 +523,7 @@
        A2DP_LDAC_ENCODER_INTERVAL_MS) /
       1000;
 
-  LOG_DEBUG(LOG_TAG, "%s: PCM bytes per tick %u", __func__,
+  LOG_DEBUG("%s: PCM bytes per tick %u", __func__,
             a2dp_ldac_encoder_cb.ldac_feeding_state.bytes_per_tick);
 }
 
@@ -544,8 +540,8 @@
   uint8_t nb_iterations = 0;
 
   a2dp_ldac_get_num_frame_iteration(&nb_iterations, &nb_frame, timestamp_us);
-  LOG_VERBOSE(LOG_TAG, "%s: Sending %d frames per iteration, %d iterations",
-              __func__, nb_frame, nb_iterations);
+  LOG_VERBOSE("%s: Sending %d frames per iteration, %d iterations", __func__,
+              nb_frame, nb_iterations);
   if (nb_frame == 0) return;
 
   for (uint8_t counter = 0; counter < nb_iterations; counter++) {
@@ -581,8 +577,7 @@
       A2DP_LDAC_MEDIA_BYTES_PER_FRAME *
       a2dp_ldac_encoder_cb.feeding_params.channel_count *
       a2dp_ldac_encoder_cb.feeding_params.bits_per_sample / 8;
-  LOG_VERBOSE(LOG_TAG, "%s: pcm_bytes_per_frame %u", __func__,
-              pcm_bytes_per_frame);
+  LOG_VERBOSE("%s: pcm_bytes_per_frame %u", __func__, pcm_bytes_per_frame);
 
   uint32_t us_this_tick = A2DP_LDAC_ENCODER_INTERVAL_MS * 1000;
   uint64_t now_us = timestamp_us;
@@ -601,8 +596,8 @@
       result * pcm_bytes_per_frame;
   nof = result;
 
-  LOG_VERBOSE(LOG_TAG, "%s: effective num of frames %u, iterations %u",
-              __func__, nof, noi);
+  LOG_VERBOSE("%s: effective num of frames %u, iterations %u", __func__, nof,
+              noi);
 
   *num_of_frames = nof;
   *num_of_iterations = noi;
@@ -654,7 +649,7 @@
         bytes_read += temp_bytes_read;
         uint8_t* packet = (uint8_t*)(p_buf + 1) + p_buf->offset + p_buf->len;
         if (a2dp_ldac_encoder_cb.ldac_handle == NULL) {
-          LOG_ERROR(LOG_TAG, "%s: invalid LDAC handle", __func__);
+          LOG_ERROR("%s: invalid LDAC handle", __func__);
           a2dp_ldac_encoder_cb.stats.media_read_total_dropped_packets++;
           osi_free(p_buf);
           return;
@@ -665,12 +660,12 @@
         if (result != 0) {
           int err_code =
               ldac_get_error_code_func(a2dp_ldac_encoder_cb.ldac_handle);
-          LOG_ERROR(LOG_TAG,
-                    "%s: LDAC encoding error: %d api_error = %d "
-                    "handle_error = %d block_error = %d error_code = 0x%x",
-                    __func__, result, LDACBT_API_ERR(err_code),
-                    LDACBT_HANDLE_ERR(err_code), LDACBT_BLOCK_ERR(err_code),
-                    err_code);
+          LOG_ERROR(
+              "%s: LDAC encoding error: %d api_error = %d "
+              "handle_error = %d block_error = %d error_code = 0x%x",
+              __func__, result, LDACBT_API_ERR(err_code),
+              LDACBT_HANDLE_ERR(err_code), LDACBT_BLOCK_ERR(err_code),
+              err_code);
           a2dp_ldac_encoder_cb.stats.media_read_total_dropped_packets++;
           osi_free(p_buf);
           return;
@@ -680,7 +675,7 @@
         nb_frame--;
         p_buf->layer_specific += out_frames;  // added a frame to the buffer
       } else {
-        LOG_WARN(LOG_TAG, "%s: underflow %d", __func__, nb_frame);
+        LOG_WARN("%s: underflow %d", __func__, nb_frame);
         a2dp_ldac_encoder_cb.ldac_feeding_state.counter +=
             nb_frame * LDACBT_ENC_LSU *
             a2dp_ldac_encoder_cb.feeding_params.channel_count *
diff --git a/stack/avct/avct_api.cc b/stack/avct/avct_api.cc
index 51e2daf..68dfec2 100644
--- a/stack/avct/avct_api.cc
+++ b/stack/avct/avct_api.cc
@@ -57,7 +57,7 @@
 
   /* register PSM with L2CAP */
   L2CA_Register(AVCT_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_appl,
-                true /* enable_snoop */);
+                true /* enable_snoop */, nullptr);
 
   /* set security level */
   BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0,
@@ -69,8 +69,15 @@
   memset(&avct_cb, 0, sizeof(tAVCT_CB));
 
   /* Include the browsing channel which uses eFCR */
+  tL2CAP_ERTM_INFO ertm_info;
+  ertm_info.preferred_mode = avct_l2c_br_fcr_opts_def.mode;
+  ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
+  ertm_info.user_rx_buf_size = BT_DEFAULT_BUFFER_SIZE;
+  ertm_info.user_tx_buf_size = BT_DEFAULT_BUFFER_SIZE;
+  ertm_info.fcr_rx_buf_size = BT_DEFAULT_BUFFER_SIZE;
+  ertm_info.fcr_tx_buf_size = BT_DEFAULT_BUFFER_SIZE;
   L2CA_Register(AVCT_BR_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_br_appl,
-                true /*enable_snoop*/);
+                true /*enable_snoop*/, &ertm_info);
 
   /* AVCTP browsing channel uses the same security service as AVCTP control
    * channel */
diff --git a/stack/avct/avct_bcb_act.cc b/stack/avct/avct_bcb_act.cc
index 616976e..aca7349 100644
--- a/stack/avct/avct_bcb_act.cc
+++ b/stack/avct/avct_bcb_act.cc
@@ -106,7 +106,7 @@
   BTM_SetOutService(p_lcb->peer_addr, BTM_SEC_SERVICE_AVCTP_BROWSE, 0);
 
   /* Set the FCR options: Browsing channel mandates ERTM */
-  ertm_info.preferred_mode = avct_l2c_br_fcr_opts_def.mode;
+  ertm_info.preferred_mode = L2CAP_FCR_ERTM_MODE;
   ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
   ertm_info.user_rx_buf_size = BT_DEFAULT_BUFFER_SIZE;
   ertm_info.user_tx_buf_size = BT_DEFAULT_BUFFER_SIZE;
diff --git a/stack/avdt/avdt_api.cc b/stack/avdt/avdt_api.cc
index 7447941..3db5fb2 100644
--- a/stack/avdt/avdt_api.cc
+++ b/stack/avdt/avdt_api.cc
@@ -91,7 +91,7 @@
 void AVDT_Register(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback) {
   /* register PSM with L2CAP */
   L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO*)&avdt_l2c_appl,
-                true /* enable_snoop */);
+                true /* enable_snoop */, nullptr);
 
   /* set security level */
   BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask,
@@ -1097,36 +1097,6 @@
 
 /*******************************************************************************
  *
- * Function         AVDT_GetSignalChannel
- *
- * Description      Get the L2CAP CID used by the signal channel of the given
- *                  handle.
- *
- * Returns          CID if successful, otherwise 0.
- *
- ******************************************************************************/
-uint16_t AVDT_GetSignalChannel(uint8_t handle, const RawAddress& bd_addr) {
-  AvdtpScb* p_scb;
-  AvdtpCcb* p_ccb;
-  uint8_t tcid = 0; /* tcid is always 0 for signal channel */
-  uint16_t lcid = 0;
-
-  /* map handle to scb */
-  if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) &&
-      ((p_ccb = p_scb->p_ccb) != NULL)) {
-    lcid = avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
-  } else {
-    p_ccb = avdt_ccb_by_bd(bd_addr);
-    if (p_ccb != NULL) {
-      lcid = avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
-    }
-  }
-
-  return (lcid);
-}
-
-/*******************************************************************************
- *
  * Function         AVDT_SendReport
  *
  * Description
diff --git a/stack/avdt/avdt_msg.cc b/stack/avdt/avdt_msg.cc
index 853f369..33fbfa7 100644
--- a/stack/avdt/avdt_msg.cc
+++ b/stack/avdt/avdt_msg.cc
@@ -985,18 +985,30 @@
  * Returns          Error code or zero if no error.
  *
  ******************************************************************************/
-static uint8_t avdt_msg_prs_rej(tAVDT_MSG* p_msg, uint8_t* p, uint8_t sig) {
-  if ((sig == AVDT_SIG_SETCONFIG) || (sig == AVDT_SIG_RECONFIG)) {
-    p_msg->hdr.err_param = *p++;
-    p_msg->hdr.err_code = *p;
-  } else if ((sig == AVDT_SIG_START) || (sig == AVDT_SIG_SUSPEND)) {
-    AVDT_MSG_PRS_SEID(p, p_msg->hdr.err_param);
-    p_msg->hdr.err_code = *p;
+static uint8_t avdt_msg_prs_rej(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len,
+                                uint8_t sig) {
+  uint8_t error = 0;
+
+  if (len > 0) {
+    if ((sig == AVDT_SIG_SETCONFIG) || (sig == AVDT_SIG_RECONFIG)) {
+      p_msg->hdr.err_param = *p++;
+      len--;
+    } else if ((sig == AVDT_SIG_START) || (sig == AVDT_SIG_SUSPEND)) {
+      AVDT_MSG_PRS_SEID(p, p_msg->hdr.err_param);
+      len--;
+    }
+  }
+
+  if (len < 1) {
+    char error_info[] = "AVDT rejected response length mismatch";
+    android_errorWriteWithInfoLog(0x534e4554, "79702484", -1, error_info,
+                                  strlen(error_info));
+    error = AVDT_ERR_LENGTH;
   } else {
     p_msg->hdr.err_code = *p;
   }
 
-  return 0;
+  return error;
 }
 
 /*******************************************************************************
@@ -1604,7 +1616,7 @@
       evt = avdt_msg_rsp_2_evt[sig - 1];
     } else /* msg_type == AVDT_MSG_TYPE_REJ */
     {
-      err = avdt_msg_prs_rej(&msg, p, sig);
+      err = avdt_msg_prs_rej(&msg, p, p_buf->len, sig);
       evt = avdt_msg_rej_2_evt[sig - 1];
     }
 
diff --git a/stack/avrc/avrc_bld_ct.cc b/stack/avrc/avrc_bld_ct.cc
index afb1784b..384ae60 100644
--- a/stack/avrc/avrc_bld_ct.cc
+++ b/stack/avrc/avrc_bld_ct.cc
@@ -431,7 +431,24 @@
   p_pkt->len = (p_data - p_start);
   return AVRC_STS_NO_ERROR;
 }
-
+static tAVRC_STS avrc_bld_get_item_attributes_cmd(
+    BT_HDR* p_pkt, const tAVRC_GET_ATTRS_CMD* cmd) {
+  AVRC_TRACE_API("%s", __func__);
+  uint8_t* p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset;
+  /* This is where the PDU specific for AVRC starts
+   * AVRCP Spec 1.4 section 22.19 */
+  uint8_t* p_data = p_start + 1; /* pdu */
+  UINT16_TO_BE_STREAM(p_data, 12 + 4 * cmd->attr_count);
+  UINT8_TO_BE_STREAM(p_data, cmd->scope);
+  uint64_t uid;
+  memcpy(&uid, cmd->uid, 8);
+  UINT64_TO_BE_STREAM(p_data, uid);
+  UINT16_TO_BE_STREAM(p_data, cmd->uid_counter);
+  UINT8_TO_BE_STREAM(p_data, cmd->attr_count);
+  ARRAY_TO_BE_STREAM(p_data, cmd->p_attr_list, 4 * cmd->attr_count);
+  p_pkt->len = (p_data - p_start);
+  return AVRC_STS_NO_ERROR;
+}
 /*******************************************************************************
  *
  * Function         avrc_bld_set_browsed_player_cmd
@@ -645,6 +662,9 @@
     case AVRC_PDU_CHANGE_PATH:
       status = avrc_bld_change_folder_cmd(p_pkt, &(p_cmd->chg_path));
       break;
+    case AVRC_PDU_GET_ITEM_ATTRIBUTES:
+      status = avrc_bld_get_item_attributes_cmd(p_pkt, &(p_cmd->get_attrs));
+      break;
     case AVRC_PDU_SET_BROWSED_PLAYER:
       status = avrc_bld_set_browsed_player_cmd(p_pkt, &(p_cmd->br_player));
       break;
diff --git a/stack/avrc/avrc_pars_ct.cc b/stack/avrc/avrc_pars_ct.cc
index 39ed921..7b64b86 100644
--- a/stack/avrc/avrc_pars_ct.cc
+++ b/stack/avrc/avrc_pars_ct.cc
@@ -425,6 +425,30 @@
       break;
     }
 
+    case AVRC_PDU_GET_ITEM_ATTRIBUTES: {
+      tAVRC_GET_ATTRS_RSP* get_attr_rsp = &(p_rsp->get_attrs);
+      get_attr_rsp->pdu = pdu;
+      BE_STREAM_TO_UINT8(get_attr_rsp->status, p)
+      BE_STREAM_TO_UINT8(get_attr_rsp->num_attrs, p);
+      get_attr_rsp->p_attrs = (tAVRC_ATTR_ENTRY*)osi_malloc(
+          get_attr_rsp->num_attrs * sizeof(tAVRC_ATTR_ENTRY));
+      for (int i = 0; i < get_attr_rsp->num_attrs; i++) {
+        tAVRC_ATTR_ENTRY* attr_entry = &(get_attr_rsp->p_attrs[i]);
+        BE_STREAM_TO_UINT32(attr_entry->attr_id, p);
+        BE_STREAM_TO_UINT16(attr_entry->name.charset_id, p);
+        BE_STREAM_TO_UINT16(attr_entry->name.str_len, p);
+        min_len += attr_entry->name.str_len;
+        if (pkt_len < min_len) goto browse_length_error;
+        attr_entry->name.p_str =
+            (uint8_t*)osi_malloc(attr_entry->name.str_len * sizeof(uint8_t));
+        BE_STREAM_TO_ARRAY(p, attr_entry->name.p_str, attr_entry->name.str_len);
+        AVRC_TRACE_DEBUG("%s media attr id %d cs %d name len %d", __func__,
+                         attr_entry->attr_id, attr_entry->name.charset_id,
+                         attr_entry->name.str_len);
+      }
+
+      break;
+    }
     case AVRC_PDU_SET_BROWSED_PLAYER: {
       tAVRC_SET_BR_PLAYER_RSP* set_br_pl_rsp = &(p_rsp->br_player);
       /* Copyback the PDU */
diff --git a/stack/avrc/avrc_pars_tg.cc b/stack/avrc/avrc_pars_tg.cc
index 22471bd..fe1db3d 100644
--- a/stack/avrc/avrc_pars_tg.cc
+++ b/stack/avrc/avrc_pars_tg.cc
@@ -363,7 +363,7 @@
  *
  * Description      This function is used to parse cmds received for CTRL
  *                  Currently it is for SetAbsVolume and Volume Change
- *                  Notification..
+ *                  Notification.
  *
  * Returns          AVRC_STS_NO_ERROR, if the message in p_data is parsed
  *                  successfully.
@@ -390,6 +390,12 @@
   return status;
 }
 
+#define RETURN_STATUS_IF_FALSE(_status_, _b_, _msg_, ...) \
+  if (!(_b_)) {                                           \
+    AVRC_TRACE_DEBUG(_msg_, ##__VA_ARGS__);               \
+    return _status_;                                      \
+  }
+
 /*******************************************************************************
  *
  * Function         avrc_pars_browsing_cmd
@@ -409,6 +415,10 @@
   uint8_t* p = p_msg->p_browse_data;
   int count;
 
+  uint16_t min_len = 3;
+  RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+                         "msg too short");
+
   p_result->pdu = *p++;
   AVRC_TRACE_DEBUG("avrc_pars_browsing_cmd() pdu:0x%x", p_result->pdu);
   /* skip over len */
@@ -416,11 +426,20 @@
 
   switch (p_result->pdu) {
     case AVRC_PDU_SET_BROWSED_PLAYER: /* 0x70 */
+      min_len += 2;
+      RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+                             "msg too short");
+
       // For current implementation all players are browsable.
       BE_STREAM_TO_UINT16(p_result->br_player.player_id, p);
       break;
 
     case AVRC_PDU_GET_FOLDER_ITEMS: /* 0x71 */
+
+      min_len += 10;
+      RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+                             "msg too short");
+
       STREAM_TO_UINT8(p_result->get_items.scope, p);
       // To be modified later here (Scope) when all browsing commands are
       // supported
@@ -441,12 +460,21 @@
         if (buf_len < (count << 2))
           p_result->get_items.attr_count = count = (buf_len >> 2);
         for (int idx = 0; idx < count; idx++) {
+          min_len += 4;
+          RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD,
+                                 (p_msg->browse_len >= min_len),
+                                 "msg too short");
+
           BE_STREAM_TO_UINT32(p_result->get_items.p_attr_list[idx], p);
         }
       }
       break;
 
     case AVRC_PDU_CHANGE_PATH: /* 0x72 */
+      min_len += 11;
+      RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+                             "msg too short");
+
       BE_STREAM_TO_UINT16(p_result->chg_path.uid_counter, p);
       BE_STREAM_TO_UINT8(p_result->chg_path.direction, p);
       if (p_result->chg_path.direction != AVRC_DIR_UP &&
@@ -457,7 +485,12 @@
       break;
 
     case AVRC_PDU_GET_ITEM_ATTRIBUTES: /* 0x73 */
+      min_len += 12;
+      RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+                             "msg too short");
+
       BE_STREAM_TO_UINT8(p_result->get_attrs.scope, p);
+
       if (p_result->get_attrs.scope > AVRC_SCOPE_NOW_PLAYING) {
         status = AVRC_STS_BAD_SCOPE;
         break;
@@ -473,6 +506,11 @@
           p_result->get_attrs.attr_count = count = (buf_len >> 2);
         for (int idx = 0, count = 0; idx < p_result->get_attrs.attr_count;
              idx++) {
+          min_len += 4;
+          RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD,
+                                 (p_msg->browse_len >= min_len),
+                                 "msg too short");
+
           BE_STREAM_TO_UINT32(p_result->get_attrs.p_attr_list[count], p);
           if (AVRC_IS_VALID_MEDIA_ATTRIBUTE(
                   p_result->get_attrs.p_attr_list[count])) {
@@ -488,6 +526,10 @@
       break;
 
     case AVRC_PDU_GET_TOTAL_NUM_OF_ITEMS: /* 0x75 */
+      ++min_len;
+      RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+                             "msg too short");
+
       BE_STREAM_TO_UINT8(p_result->get_num_of_items.scope, p);
       if (p_result->get_num_of_items.scope > AVRC_SCOPE_NOW_PLAYING) {
         status = AVRC_STS_BAD_SCOPE;
@@ -495,6 +537,10 @@
       break;
 
     case AVRC_PDU_SEARCH: /* 0x80 */
+      min_len += 4;
+      RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+                             "msg too short");
+
       BE_STREAM_TO_UINT16(p_result->search.string.charset_id, p);
       BE_STREAM_TO_UINT16(p_result->search.string.str_len, p);
       p_result->search.string.p_str = p_buf;
@@ -504,6 +550,10 @@
         } else {
           android_errorWriteLog(0x534e4554, "63146237");
         }
+        min_len += p_result->search.string.str_len;
+        RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+                               "msg too short");
+
         BE_STREAM_TO_ARRAY(p, p_buf, p_result->search.string.str_len);
       } else {
         status = AVRC_STS_INTERNAL_ERR;
diff --git a/stack/bnep/bnep_main.cc b/stack/bnep/bnep_main.cc
index 265ec79..99124cf 100644
--- a/stack/bnep/bnep_main.cc
+++ b/stack/bnep/bnep_main.cc
@@ -94,8 +94,8 @@
   bnep_cb.reg_info.pL2CA_CongestionStatus_Cb = bnep_congestion_ind;
 
   /* Now, register with L2CAP */
-  if (!L2CA_Register(BT_PSM_BNEP, &bnep_cb.reg_info,
-                     false /* enable_snoop */)) {
+  if (!L2CA_Register(BT_PSM_BNEP, &bnep_cb.reg_info, false /* enable_snoop */,
+                     nullptr)) {
     BNEP_TRACE_ERROR("BNEP - Registration failed");
     return BNEP_SECURITY_FAIL;
   }
diff --git a/stack/btm/btm_acl.cc b/stack/btm/btm_acl.cc
index 2a417b9..e2ae492 100644
--- a/stack/btm/btm_acl.cc
+++ b/stack/btm/btm_acl.cc
@@ -51,6 +51,8 @@
 #include "hcidefs.h"
 #include "hcimsgs.h"
 #include "l2c_int.h"
+#include "main/shim/btm_api.h"
+#include "main/shim/shim.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 
@@ -543,8 +545,8 @@
   tBTM_PM_MODE pwr_mode;
   tBTM_PM_PWR_MD settings;
 
-  LOG_INFO(LOG_TAG, "%s: peer %s new_role=0x%x p_cb=%p p_switch_role_cb=%p",
-           __func__, remote_bd_addr.ToString().c_str(), new_role, p_cb,
+  LOG_INFO("%s: peer %s new_role=0x%x p_cb=%p p_switch_role_cb=%p", __func__,
+           remote_bd_addr.ToString().c_str(), new_role, p_cb,
            btm_cb.devcb.p_switch_role_cb);
 
   /* Make sure the local device supports switching */
@@ -1085,7 +1087,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_read_remote_ext_features_complete(uint8_t* p) {
+void btm_read_remote_ext_features_complete(uint8_t* p, uint8_t evt_len) {
   tACL_CONN* p_acl_cb;
   uint8_t page_num, max_page;
   uint16_t handle;
@@ -1093,6 +1095,14 @@
 
   BTM_TRACE_DEBUG("btm_read_remote_ext_features_complete");
 
+  if (evt_len < HCI_EXT_FEATURES_SUCCESS_EVT_LEN) {
+    android_errorWriteLog(0x534e4554, "141552859");
+    BTM_TRACE_ERROR(
+        "btm_read_remote_ext_features_complete evt length too short. length=%d",
+        evt_len);
+    return;
+  }
+
   ++p;
   STREAM_TO_UINT16(handle, p);
   STREAM_TO_UINT8(page_num, p);
@@ -1112,6 +1122,19 @@
     return;
   }
 
+  if (page_num > HCI_EXT_FEATURES_PAGE_MAX) {
+    android_errorWriteLog(0x534e4554, "141552859");
+    BTM_TRACE_ERROR("btm_read_remote_ext_features_complete num_page=%d invalid",
+                    page_num);
+    return;
+  }
+
+  if (page_num > max_page) {
+    BTM_TRACE_WARNING(
+        "btm_read_remote_ext_features_complete num_page=%d, max_page=%d "
+        "invalid", page_num, max_page);
+  }
+
   p_acl_cb = &btm_cb.acl_db[acl_idx];
 
   /* Copy the received features page */
@@ -1361,6 +1384,10 @@
  ******************************************************************************/
 uint16_t BTM_GetHCIConnHandle(const RawAddress& remote_bda,
                               tBT_TRANSPORT transport) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_GetHCIConnHandle(remote_bda, transport);
+  }
+
   tACL_CONN* p;
   BTM_TRACE_DEBUG("BTM_GetHCIConnHandle");
   p = btm_bda_to_acl(remote_bda, transport);
@@ -1749,65 +1776,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadRemoteExtendedFeatures
- *
- * Returns          pointer to the remote extended features mask (8 bytes)
- *                  or NULL if bad page
- *
- ******************************************************************************/
-uint8_t* BTM_ReadRemoteExtendedFeatures(const RawAddress& addr,
-                                        uint8_t page_number) {
-  tACL_CONN* p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR);
-  BTM_TRACE_DEBUG("BTM_ReadRemoteExtendedFeatures");
-  if (p == NULL) {
-    return (NULL);
-  }
-
-  if (page_number > HCI_EXT_FEATURES_PAGE_MAX) {
-    BTM_TRACE_ERROR("Warning: BTM_ReadRemoteExtendedFeatures page %d unknown",
-                    page_number);
-    return NULL;
-  }
-
-  return (p->peer_lmp_feature_pages[page_number]);
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadNumberRemoteFeaturesPages
- *
- * Returns          number of features pages read from the remote device.
- *
- ******************************************************************************/
-uint8_t BTM_ReadNumberRemoteFeaturesPages(const RawAddress& addr) {
-  tACL_CONN* p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR);
-  BTM_TRACE_DEBUG("BTM_ReadNumberRemoteFeaturesPages");
-  if (p == NULL) {
-    return (0);
-  }
-
-  return (p->num_read_pages);
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadAllRemoteFeatures
- *
- * Returns          pointer to all features of the remote (24 bytes).
- *
- ******************************************************************************/
-uint8_t* BTM_ReadAllRemoteFeatures(const RawAddress& addr) {
-  tACL_CONN* p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR);
-  BTM_TRACE_DEBUG("BTM_ReadAllRemoteFeatures");
-  if (p == NULL) {
-    return (NULL);
-  }
-
-  return (p->peer_lmp_feature_pages[0]);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_RegBusyLevelNotif
  *
  * Description      This function is called to register a callback to receive
@@ -1835,40 +1803,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetQoS
- *
- * Description      This function is called to setup QoS
- *
- * Returns          status of the operation
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetQoS(const RawAddress& bd, FLOW_SPEC* p_flow,
-                       tBTM_CMPL_CB* p_cb) {
-  tACL_CONN* p = &btm_cb.acl_db[0];
-
-  VLOG(2) << __func__ << " BdAddr: " << bd;
-
-  /* If someone already waiting on the version, do not allow another */
-  if (btm_cb.devcb.p_qos_setup_cmpl_cb) return (BTM_BUSY);
-
-  p = btm_bda_to_acl(bd, BT_TRANSPORT_BR_EDR);
-  if (p != NULL) {
-    btm_cb.devcb.p_qos_setup_cmpl_cb = p_cb;
-    alarm_set_on_mloop(btm_cb.devcb.qos_setup_timer, BTM_DEV_REPLY_TIMEOUT_MS,
-                       btm_qos_setup_timeout, NULL);
-
-    btsnd_hcic_qos_setup(p->hci_handle, p_flow->qos_flags, p_flow->service_type,
-                         p_flow->token_rate, p_flow->peak_bandwidth,
-                         p_flow->latency, p_flow->delay_variation);
-    return (BTM_CMD_STARTED);
-  }
-
-  /* If here, no BD Addr found */
-  return (BTM_UNKNOWN_ADDR);
-}
-
-/*******************************************************************************
- *
  * Function         btm_qos_setup_timeout
  *
  * Description      Callback when QoS setup times out.
@@ -2040,39 +1974,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadLinkQuality
- *
- * Description      This function is called to read the link qulaity.
- *                  The value of the link quality is returned in the callback.
- *                  (tBTM_LINK_QUALITY_RESULT)
- *
- * Returns          BTM_CMD_STARTED if successfully initiated or error code
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ReadLinkQuality(const RawAddress& remote_bda,
-                                tBTM_CMPL_CB* p_cb) {
-  VLOG(2) << __func__ << ": RemBdAddr: " << remote_bda;
-
-  /* If someone already waiting on the version, do not allow another */
-  if (btm_cb.devcb.p_link_qual_cmpl_cb) return (BTM_BUSY);
-
-  tACL_CONN* p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR);
-  if (p != (tACL_CONN*)NULL) {
-    btm_cb.devcb.p_link_qual_cmpl_cb = p_cb;
-    alarm_set_on_mloop(btm_cb.devcb.read_link_quality_timer,
-                       BTM_DEV_REPLY_TIMEOUT_MS, btm_read_link_quality_timeout,
-                       NULL);
-
-    btsnd_hcic_get_link_quality(p->hci_handle);
-    return (BTM_CMD_STARTED);
-  }
-
-  /* If here, no BD Addr found */
-  return (BTM_UNKNOWN_ADDR);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_ReadTxPower
  *
  * Description      This function is called to read the current
diff --git a/stack/btm/btm_ble_addr.cc b/stack/btm/btm_ble_addr.cc
index 000066e..678dd76 100644
--- a/stack/btm/btm_ble_addr.cc
+++ b/stack/btm/btm_ble_addr.cc
@@ -72,7 +72,7 @@
   p_cb->own_addr_type = BLE_ADDR_RANDOM;
 
   /* start a periodical timer to refresh random addr */
-  uint64_t interval_ms = BTM_BLE_PRIVATE_ADDR_INT_MS;
+  uint64_t interval_ms = btm_get_next_private_addrress_interval_ms();
 #if (BTM_BLE_CONFORMANCE_TESTING == TRUE)
   interval_ms = btm_cb.ble_ctr_cb.rpa_tout * 1000;
 #endif
@@ -93,6 +93,14 @@
       std::move(cb)));
 }
 
+uint64_t btm_get_next_private_addrress_interval_ms() {
+  /* 7 minutes minimum, 15 minutes maximum for random address refreshing */
+  const uint64_t interval_min_ms = (7 * 60 * 1000);
+  const uint64_t interval_random_part_max_ms = (8 * 60 * 1000);
+
+  return interval_min_ms + std::rand() % interval_random_part_max_ms;
+}
+
 /*******************************************************************************
  *
  * Function         btm_gen_non_resolve_paddr_cmpl
diff --git a/stack/btm/btm_ble_batchscan.cc b/stack/btm/btm_ble_batchscan.cc
index f7d5d3c5..3d89358 100644
--- a/stack/btm/btm_ble_batchscan.cc
+++ b/stack/btm/btm_ble_batchscan.cc
@@ -63,6 +63,7 @@
 
   uint8_t sub_event = 0;
   tBTM_BLE_VSC_CB cmn_ble_vsc_cb;
+  if (len == 0) return;
   STREAM_TO_UINT8(sub_event, p);
 
   BTM_TRACE_EVENT(
@@ -90,6 +91,7 @@
 
       /* Extract the adv info details */
       if (ADV_INFO_PRESENT == adv_data.advertiser_info_present) {
+        if (len < 15) return;
         STREAM_TO_UINT8(adv_data.tx_power, p);
         STREAM_TO_UINT8(adv_data.rssi_value, p);
         STREAM_TO_UINT16(adv_data.time_stamp, p);
diff --git a/stack/btm/btm_ble_gap.cc b/stack/btm/btm_ble_gap.cc
index f714f412..ca424b4 100644
--- a/stack/btm/btm_ble_gap.cc
+++ b/stack/btm/btm_ble_gap.cc
@@ -49,6 +49,7 @@
 #include "gattdefs.h"
 #include "l2c_int.h"
 #include "osi/include/log.h"
+#include "common/time_util.h"
 
 #include "main/shim/btm_api.h"
 #include "main/shim/shim.h"
@@ -87,6 +88,14 @@
     return items.front().data;
   }
 
+  bool Exist(uint8_t addr_type, const RawAddress& addr) {
+    auto it = Find(addr_type, addr);
+    if (it != items.end()) {
+        return true;
+    }
+    return false;
+  }
+
   /* Append |data| for device |addr_type, addr| */
   const std::vector<uint8_t>& Append(uint8_t addr_type, const RawAddress& addr,
                                      std::vector<uint8_t> data) {
@@ -112,6 +121,10 @@
     }
   }
 
+  void ClearAll() {
+    items.clear();
+  }
+
  private:
   struct Item {
     uint8_t addr_type;
@@ -447,6 +460,7 @@
     /* scan is not started */
     if (!BTM_BLE_IS_SCAN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) {
       /* allow config of scan type */
+      cache.ClearAll();
       p_inq->scan_type = (p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE)
                              ? BTM_BLE_SCAN_MODE_ACTI
                              : p_inq->scan_type;
@@ -711,6 +725,9 @@
  ******************************************************************************/
 bool BTM_BleLocalPrivacyEnabled(void) {
 #if (BLE_PRIVACY_SPT == TRUE)
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleLocalPrivacyEnabled();
+  }
   return (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE);
 #else
   return false;
@@ -1045,7 +1062,7 @@
 
   btm_ble_update_dmt_flag_bits(&flag, connect_mode, disc_mode);
 
-  LOG_DEBUG(LOG_TAG, "disc_mode %04x", disc_mode);
+  LOG_DEBUG("disc_mode %04x", disc_mode);
   /* update discoverable flag */
   if (disc_mode & BTM_BLE_LIMITED_DISCOVERABLE) {
     flag &= ~BTM_BLE_GEN_DISC_FLAG;
@@ -1294,6 +1311,7 @@
   }
 
   if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) {
+    cache.ClearAll();
     btm_send_hci_set_scan_params(
         BTM_BLE_SCAN_MODE_ACTI, BTM_BLE_LOW_LATENCY_SCAN_INT,
         BTM_BLE_LOW_LATENCY_SCAN_WIN,
@@ -1302,6 +1320,7 @@
     /* enable IRK list */
     btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN);
 #endif
+    p_ble_cb->inq_var.scan_type = BTM_BLE_SCAN_MODE_ACTI;
     p_ble_cb->inq_var.scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE;
     status = btm_ble_start_scan();
   } else if ((p_ble_cb->inq_var.scan_interval !=
@@ -1652,7 +1671,7 @@
   tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
 
   /* Save the info */
-  p_cur->inq_result_type = BTM_INQ_RESULT_BLE;
+  p_cur->inq_result_type |= BTM_INQ_RESULT_BLE;
   p_cur->ble_addr_type = addr_type;
   p_cur->rssi = rssi;
   p_cur->ble_primary_phy = primary_phy;
@@ -1889,18 +1908,23 @@
     btm_ble_process_adv_addr(bda, &addr_type);
 
     uint16_t event_type;
-    if (legacy_evt_type == 0x00) {  // ADV_IND;
-      event_type = 0x0013;
-    } else if (legacy_evt_type == 0x01) {  // ADV_DIRECT_IND;
-      event_type = 0x0015;
-    } else if (legacy_evt_type == 0x02) {  // ADV_SCAN_IND;
-      event_type = 0x0012;
-    } else if (legacy_evt_type == 0x03) {  // ADV_NONCONN_IND;
-      event_type = 0x0010;
-    } else if (legacy_evt_type == 0x04) {  // SCAN_RSP;
+    event_type = 1 << BLE_EVT_LEGACY_BIT;
+    if (legacy_evt_type == BTM_BLE_ADV_IND_EVT) {
+      event_type |= (1 << BLE_EVT_CONNECTABLE_BIT)|
+                    (1 << BLE_EVT_SCANNABLE_BIT);
+    } else if (legacy_evt_type == BTM_BLE_ADV_DIRECT_IND_EVT) {
+      event_type |= (1 << BLE_EVT_CONNECTABLE_BIT)|
+                    (1 << BLE_EVT_DIRECTED_BIT);
+    } else if (legacy_evt_type == BTM_BLE_ADV_SCAN_IND_EVT) {
+      event_type |= (1 << BLE_EVT_SCANNABLE_BIT);
+    } else if (legacy_evt_type == BTM_BLE_ADV_NONCONN_IND_EVT) {
+      event_type = (1 << BLE_EVT_LEGACY_BIT);//0x0010;
+    } else if (legacy_evt_type == BTM_BLE_SCAN_RSP_EVT) {  // SCAN_RSP;
       // We can't distinguish between "SCAN_RSP to an ADV_IND", and "SCAN_RSP to
       // an ADV_SCAN_IND", so always return "SCAN_RSP to an ADV_IND"
-      event_type = 0x001B;
+      event_type |= (1 << BLE_EVT_CONNECTABLE_BIT)|
+                    (1 << BLE_EVT_SCANNABLE_BIT)|
+                    (1 << BLE_EVT_SCAN_RESPONSE_BIT);
     } else {
       BTM_TRACE_ERROR(
           "Malformed LE Advertising Report Event - unsupported "
@@ -1934,11 +1958,19 @@
 
   bool is_scannable = ble_evt_type_is_scannable(evt_type);
   bool is_scan_resp = ble_evt_type_is_scan_resp(evt_type);
+  bool is_legacy = ble_evt_type_is_legacy(evt_type);
 
-  bool is_start =
-      ble_evt_type_is_legacy(evt_type) && is_scannable && !is_scan_resp;
+  // We might receive a legacy scan response without receving a ADV_IND
+  // or ADV_SCAN_IND before. Only parsing the scan response data which
+  // has no ad flag, the device will be set to DUMO mode. The createbond
+  // procedure will use the wrong device mode.
+  // In such case no necessary to report scan response
+  if(is_legacy && is_scan_resp && !cache.Exist(addr_type, bda))
+    return;
 
-  if (ble_evt_type_is_legacy(evt_type))
+  bool is_start = is_legacy && is_scannable && !is_scan_resp;
+
+  if (is_legacy)
     AdvertiseDataParser::RemoveTrailingZeros(tmp);
 
   // We might have send scan request to this device before, but didn't get the
@@ -1983,6 +2015,7 @@
       update = false;
     } else {
       /* if yes, skip it */
+      cache.Clear(addr_type, bda);
       return; /* assumption: one result per event */
     }
   }
@@ -1992,11 +2025,13 @@
     p_i = btm_inq_db_new(bda);
     if (p_i != NULL) {
       p_inq->inq_cmpl_info.num_resp++;
+      p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
     } else
       return;
   } else if (p_i->inq_count !=
              p_inq->inq_counter) /* first time seen in this inquiry */
   {
+    p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
     p_inq->inq_cmpl_info.num_resp++;
   }
 
@@ -2008,8 +2043,7 @@
   uint8_t result = btm_ble_is_discoverable(bda, adv_data);
   if (result == 0) {
     cache.Clear(addr_type, bda);
-    LOG_WARN(LOG_TAG,
-             "%s device no longer discoverable, discarding advertising packet",
+    LOG_WARN("%s device no longer discoverable, discarding advertising packet",
              __func__);
     return;
   }
diff --git a/stack/btm/btm_ble_int.h b/stack/btm/btm_ble_int.h
index fc1bfcf..b2702fe 100644
--- a/stack/btm/btm_ble_int.h
+++ b/stack/btm/btm_ble_int.h
@@ -139,6 +139,7 @@
 extern tBTM_SEC_DEV_REC* btm_ble_resolve_random_addr(
     const RawAddress& random_bda);
 extern void btm_gen_resolve_paddr_low(const RawAddress& address);
+extern uint64_t btm_get_next_private_addrress_interval_ms();
 
 /*  privacy function */
 #if (BLE_PRIVACY_SPT == TRUE)
diff --git a/stack/btm/btm_ble_int_types.h b/stack/btm/btm_ble_int_types.h
index d2fa554..375c2b3 100644
--- a/stack/btm/btm_ble_int_types.h
+++ b/stack/btm/btm_ble_int_types.h
@@ -119,9 +119,6 @@
 #define BTM_BLE_ISVALID_PARAM(x, min, max) \
   (((x) >= (min) && (x) <= (max)) || ((x) == BTM_BLE_CONN_PARAM_UNDEF))
 
-/* 15 minutes minimum for random address refreshing */
-#define BTM_BLE_PRIVATE_ADDR_INT_MS (15 * 60 * 1000)
-
 typedef struct {
   uint16_t discoverable_mode;
   uint16_t connectable_mode;
diff --git a/stack/btm/btm_ble_multi_adv.cc b/stack/btm/btm_ble_multi_adv.cc
index 120b384..d52e2a5 100644
--- a/stack/btm/btm_ble_multi_adv.cc
+++ b/stack/btm/btm_ble_multi_adv.cc
@@ -24,6 +24,7 @@
 #include "ble_advertiser.h"
 #include "ble_advertiser_hci_interface.h"
 #include "btm_int_types.h"
+#include "stack/btm/btm_ble_int.h"
 
 #include <string.h>
 #include <queue>
@@ -257,7 +258,7 @@
               p_inst->own_address = bda;
 
               alarm_set_on_mloop(p_inst->adv_raddr_timer,
-                                 BTM_BLE_PRIVATE_ADDR_INT_MS,
+                                 btm_get_next_private_addrress_interval_ms(),
                                  btm_ble_adv_raddr_timer_timeout, p_inst);
               cb.Run(p_inst->inst_id, BTM_BLE_MULTI_ADV_SUCCESS);
             },
@@ -429,7 +430,8 @@
             c->self->adv_inst[c->inst_id].tx_power = tx_power;
 
             if (c->self->adv_inst[c->inst_id].own_address_type == BLE_ADDR_PUBLIC) {
-              c->self->StartAdvertisingSetAfterAddressPart(std::move(c));
+              auto self = c->self;
+              self->StartAdvertisingSetAfterAddressPart(std::move(c));
               return;
             }
 
@@ -449,7 +451,8 @@
                   return;
                 }
 
-                c->self->StartAdvertisingSetAfterAddressPart(std::move(c));
+                auto self = c->self;
+                self->StartAdvertisingSetAfterAddressPart(std::move(c));
           }, base::Passed(&c)));
         }, base::Passed(&c)));
     }, base::Passed(&c)));
@@ -492,11 +495,11 @@
                           return;
                         }
 
+                        auto self = c->self;
                         if (c->periodic_params.enable) {
-                          c->self->StartAdvertisingSetPeriodicPart(
-                              std::move(c));
+                          self->StartAdvertisingSetPeriodicPart(std::move(c));
                         } else {
-                          c->self->StartAdvertisingSetFinish(std::move(c));
+                          self->StartAdvertisingSetFinish(std::move(c));
                         }
                       },
                       base::Passed(&c)));
@@ -550,7 +553,8 @@
                   return;
                 }
 
-                c->self->StartAdvertisingSetFinish(std::move(c));
+                auto self = c->self;
+                self->StartAdvertisingSetFinish(std::move(c));
 
               }, base::Passed(&c)));
         }, base::Passed(&c)));
@@ -689,13 +693,17 @@
     p_inst->advertising_interval = p_params->adv_int_min;
     const RawAddress& peer_address = RawAddress::kEmpty;
 
+    // sid must be in range 0x00 to 0x0F. Since no controller supports more than
+    // 16 advertisers, it's safe to make sid equal to inst_id.
+    uint8_t sid = p_inst->inst_id % 0x0F;
+
     GetHciInterface()->SetParameters(
         p_inst->inst_id, p_params->advertising_event_properties,
         p_params->adv_int_min, p_params->adv_int_max, p_params->channel_map,
         p_inst->own_address_type, p_inst->own_address, 0x00, peer_address,
         p_params->adv_filter_policy, p_inst->tx_power,
         p_params->primary_advertising_phy, 0x00,
-        p_params->secondary_advertising_phy, 0x01 /* TODO: proper SID */,
+        p_params->secondary_advertising_phy, sid,
         p_params->scan_request_notification_enable, cb);
 
     // TODO: re-enable only if it was enabled, properly call
@@ -728,16 +736,13 @@
       data.insert(data.begin(), flags.begin(), flags.end());
     }
 
-    // Find and fill TX Power with the correct value
-    if (data.size()) {
-      size_t i = 0;
-      while (i < data.size()) {
-        uint8_t type = data[i + 1];
-        if (type == HCI_EIR_TX_POWER_LEVEL_TYPE) {
-          data[i + 2] = adv_inst[inst_id].tx_power;
-        }
-        i += data[i] + 1;
+    // Find and fill TX Power with the correct value.
+    // The TX Power section is a 3 byte section.
+    for (size_t i = 0; (i + 2) < data.size();) {
+      if (data[i + 1] == HCI_EIR_TX_POWER_LEVEL_TYPE) {
+        data[i + 2] = adv_inst[inst_id].tx_power;
       }
+      i += data[i] + 1;
     }
 
     VLOG(1) << "data is: " << base::HexEncode(data.data(), data.size());
@@ -791,8 +796,9 @@
     int length = moreThanOnePacket ? ADV_DATA_LEN_MAX : dataSize - offset;
     int newOffset = offset + length;
 
+    auto dataData = data.data();
     sender.Run(
-        inst_id, operation, length, data.data() + offset,
+        inst_id, operation, length, dataData + offset,
         Bind(&BleAdvertisingManagerImpl::DivideAndSendDataRecursively, false,
              inst_id, std::move(data), newOffset, std::move(done_cb), sender));
   }
diff --git a/stack/btm/btm_dev.cc b/stack/btm/btm_dev.cc
index d5e2850..0d9f737 100644
--- a/stack/btm/btm_dev.cc
+++ b/stack/btm/btm_dev.cc
@@ -36,6 +36,8 @@
 #include "hcidefs.h"
 #include "hcimsgs.h"
 #include "l2c_api.h"
+#include "main/shim/btm_api.h"
+#include "main/shim/shim.h"
 
 /*******************************************************************************
  *
@@ -166,6 +168,10 @@
  * Returns true if removed OK, false if not found or ACL link is active.
  */
 bool BTM_SecDeleteDevice(const RawAddress& bd_addr) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecDeleteDevice(bd_addr);
+  }
+
   if (BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_LE) ||
       BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_BR_EDR)) {
     BTM_TRACE_WARNING("%s FAILED: Cannot Delete when connection is active",
diff --git a/stack/btm/btm_devctl.cc b/stack/btm/btm_devctl.cc
index 5d4a4aa..52b66b4 100644
--- a/stack/btm/btm_devctl.cc
+++ b/stack/btm/btm_devctl.cc
@@ -199,14 +199,17 @@
 
   l2c_link_processs_num_bufs(controller->get_acl_buffer_count_classic());
 
+  // setup the random number generator
+  std::srand(std::time(nullptr));
+
 #if (BLE_PRIVACY_SPT == TRUE)
   /* Set up the BLE privacy settings */
   if (controller->supports_ble() && controller->supports_ble_privacy() &&
       controller->get_ble_resolving_list_max_size() > 0) {
     btm_ble_resolving_list_init(controller->get_ble_resolving_list_max_size());
     /* set the default random private address timeout */
-    btsnd_hcic_ble_set_rand_priv_addr_timeout(BTM_BLE_PRIVATE_ADDR_INT_MS /
-                                              1000);
+    btsnd_hcic_ble_set_rand_priv_addr_timeout(
+        btm_get_next_private_addrress_interval_ms() / 1000);
   }
 #endif
 
@@ -901,9 +904,6 @@
   length--;
 
   if (sub_event == HCI_VSE_SUBCODE_BQR_SUB_EVT) {
-    LOG(INFO) << __func__
-              << ": BQR sub event, report length: " << std::to_string(length);
-
     if (btm_cb.p_bqr_report_receiver == nullptr) {
       LOG(WARNING) << __func__ << ": No registered report receiver.";
       return;
diff --git a/stack/btm/btm_inq.cc b/stack/btm/btm_inq.cc
index 0d21c2e..800025c 100644
--- a/stack/btm/btm_inq.cc
+++ b/stack/btm/btm_inq.cc
@@ -25,6 +25,7 @@
  *
  ******************************************************************************/
 
+#include <log/log.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -120,7 +121,7 @@
 void btm_clr_inq_result_flt(void);
 
 static uint8_t btm_convert_uuid_to_eir_service(uint16_t uuid16);
-static void btm_set_eir_uuid(uint8_t* p_eir, tBTM_INQ_RESULTS* p_results);
+void btm_set_eir_uuid(uint8_t* p_eir, tBTM_INQ_RESULTS* p_results);
 static const uint8_t* btm_eir_get_uuid_list(uint8_t* p_eir, size_t eir_len,
                                             uint8_t uuid_size,
                                             uint8_t* p_num_uuid,
@@ -388,126 +389,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetPeriodicInquiryMode
- *
- * Description      This function is called to set the device periodic inquiry
- *                  mode. If the duration is zero, the periodic inquiry mode is
- *                  cancelled.
- *
- *                  Note: We currently do not allow concurrent inquiry and
- *                  periodic inquiry.
- *
- * Parameters:      p_inqparms - pointer to the inquiry information
- *                      mode - GENERAL or LIMITED inquiry
- *                      duration - length in 1.28 sec intervals (If '0', the
- *                                 inquiry is CANCELLED)
- *                      max_resps - maximum amount of devices to search for
- *                                  before ending the inquiry
- *                      filter_cond_type - BTM_CLR_INQUIRY_FILTER,
- *                                         BTM_FILTER_COND_DEVICE_CLASS, or
- *                                         BTM_FILTER_COND_BD_ADDR
- *                      filter_cond - value for the filter (based on
- *                                                          filter_cond_type)
- *
- *                  max_delay - maximum amount of time between successive
- *                              inquiries
- *                  min_delay - minimum amount of time between successive
- *                              inquiries
- *                  p_results_cb - callback returning pointer to results
- *                              (tBTM_INQ_RESULTS)
- *
- * Returns          BTM_CMD_STARTED if successfully started
- *                  BTM_ILLEGAL_VALUE if a bad parameter is detected
- *                  BTM_NO_RESOURCES if could not allocate a message buffer
- *                  BTM_SUCCESS - if cancelling the periodic inquiry
- *                  BTM_BUSY - if an inquiry is already active
- *                  BTM_WRONG_MODE if the device is not up.
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetPeriodicInquiryMode(tBTM_INQ_PARMS* p_inqparms,
-                                       uint16_t max_delay, uint16_t min_delay,
-                                       tBTM_INQ_RESULTS_CB* p_results_cb) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::BTM_SetPeriodicInquiryMode(p_inqparms, max_delay,
-                                                       min_delay, p_results_cb);
-  }
-
-  tBTM_STATUS status;
-  tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
-
-  BTM_TRACE_API(
-      "BTM_SetPeriodicInquiryMode: mode: %d, dur: %d, rsps: %d, flt: %d, min: "
-      "%d, max: %d",
-      p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps,
-      p_inqparms->filter_cond_type, min_delay, max_delay);
-
-  /*** Make sure the device is ready ***/
-  if (!BTM_IsDeviceUp()) return (BTM_WRONG_MODE);
-
-  /* Only one active inquiry is allowed in this implementation.
-     Also do not allow an inquiry if the inquiry filter is being updated */
-  if (p_inq->inq_active || p_inq->inqfilt_active) return (BTM_BUSY);
-
-  /* If illegal parameters return false */
-  if (p_inqparms->mode != BTM_GENERAL_INQUIRY &&
-      p_inqparms->mode != BTM_LIMITED_INQUIRY)
-    return (BTM_ILLEGAL_VALUE);
-
-  /* Verify the parameters for this command */
-  if (p_inqparms->duration < BTM_MIN_INQUIRY_LEN ||
-      p_inqparms->duration > BTM_MAX_INQUIRY_LENGTH ||
-      min_delay <= p_inqparms->duration ||
-      min_delay < BTM_PER_INQ_MIN_MIN_PERIOD ||
-      min_delay > BTM_PER_INQ_MAX_MIN_PERIOD || max_delay <= min_delay ||
-      max_delay < BTM_PER_INQ_MIN_MAX_PERIOD)
-  /*       max_delay > BTM_PER_INQ_MAX_MAX_PERIOD)*/
-  /*  BTM_PER_INQ_MAX_MAX_PERIOD set to 1's in all bits. Condition resulting in
-     false always*/
-  {
-    return (BTM_ILLEGAL_VALUE);
-  }
-
-  /* Save the inquiry parameters to be used upon the completion of
-   * setting/clearing the inquiry filter */
-  p_inq->inqparms = *p_inqparms;
-  p_inq->per_min_delay = min_delay;
-  p_inq->per_max_delay = max_delay;
-  p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */
-  p_inq->p_inq_results_cb = p_results_cb;
-
-  p_inq->inq_active = (uint8_t)(
-      (p_inqparms->mode == BTM_LIMITED_INQUIRY)
-          ? (BTM_LIMITED_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE)
-          : (BTM_GENERAL_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE));
-
-  /* If a filter is specified, then save it for later and clear the current
-     filter.
-     The setting of the filter is done upon completion of clearing of the
-     previous
-     filter.
-  */
-  if (p_inqparms->filter_cond_type != BTM_CLR_INQUIRY_FILTER) {
-    p_inq->state = BTM_INQ_CLR_FILT_STATE;
-    p_inqparms->filter_cond_type = BTM_CLR_INQUIRY_FILTER;
-  } else /* The filter is not being used so simply clear it; the inquiry can
-            start after this operation */
-    p_inq->state = BTM_INQ_SET_FILT_STATE;
-
-  /* Before beginning the inquiry the current filter must be cleared, so
-   * initiate the command */
-  status = btm_set_inq_event_filter(p_inqparms->filter_cond_type,
-                                    &p_inqparms->filter_cond);
-  if (status != BTM_CMD_STARTED) {
-    /* If set filter command is not succesful reset the state */
-    p_inq->p_inq_results_cb = NULL;
-    p_inq->state = BTM_INQ_INACTIVE_STATE;
-  }
-
-  return (status);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_CancelPeriodicInquiry
  *
  * Description      This function cancels a periodic inquiry
@@ -792,18 +673,8 @@
   /* Only one active inquiry is allowed in this implementation.
      Also do not allow an inquiry if the inquiry filter is being updated */
   if (p_inq->inq_active || p_inq->inqfilt_active) {
-    /*check if LE observe is already running*/
-    if (p_inq->scan_type == INQ_LE_OBSERVE &&
-        p_inq->p_inq_ble_results_cb != nullptr) {
-      BTM_TRACE_API("BTM_StartInquiry: LE observe in progress");
-      p_inq->scan_type = INQ_GENERAL;
-      p_inq->inq_active = BTM_INQUIRY_INACTIVE;
-      btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE;
-      btm_send_hci_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE);
-    } else {
-      LOG(ERROR) << __func__ << ": BTM_BUSY";
-      return (BTM_BUSY);
-    }
+    LOG(ERROR) << __func__ << ": BTM_BUSY";
+    return (BTM_BUSY);
   } else {
     p_inq->scan_type = INQ_GENERAL;
   }
@@ -971,10 +842,9 @@
   /* Make sure there is not already one in progress */
   if (p_inq->remname_active) {
     if (BTM_UseLeLink(p_inq->remname_bda)) {
-      if (btm_ble_cancel_remote_name(p_inq->remname_bda))
-        return (BTM_CMD_STARTED);
-      else
-        return (BTM_UNKNOWN_ADDR);
+      /* Cancel remote name request for LE device, and process remote name
+       * callback. */
+      btm_inq_rmt_name_failed_cancelled();
     } else
       btsnd_hcic_rmt_name_req_cancel(p_inq->remname_bda);
     return (BTM_CMD_STARTED);
@@ -1085,33 +955,6 @@
 }
 
 /*******************************************************************************
- *
- * Function         BTM_ReadInquiryRspTxPower
- *
- * Description      This command will read the inquiry Transmit Power level used
- *                  to transmit the FHS and EIR data packets. This can be used
- *                  directly in the Tx Power Level EIR data type.
- *
- * Returns          BTM_SUCCESS if successful
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ReadInquiryRspTxPower(tBTM_CMPL_CB* p_cb) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::BTM_ReadInquiryRspTxPower(p_cb);
-  }
-
-  if (btm_cb.devcb.p_inq_tx_power_cmpl_cb) return (BTM_BUSY);
-
-  btm_cb.devcb.p_inq_tx_power_cmpl_cb = p_cb;
-  alarm_set_on_mloop(btm_cb.devcb.read_inq_tx_power_timer,
-                     BTM_INQ_REPLY_TIMEOUT_MS, btm_read_inq_tx_power_timeout,
-                     NULL);
-
-  btsnd_hcic_read_inq_tx_power();
-  return (BTM_CMD_STARTED);
-}
-
-/*******************************************************************************
  *******************************************************************************
  *                                                                            **
  *                    BTM Internal Inquiry Functions                          **
@@ -1663,7 +1506,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_process_inq_results(uint8_t* p, uint8_t inq_res_mode) {
+void btm_process_inq_results(uint8_t* p, uint8_t hci_evt_len,
+                             uint8_t inq_res_mode) {
   uint8_t num_resp, xx;
   RawAddress bda;
   tINQ_DB_ENT* p_i;
@@ -1692,10 +1536,29 @@
 
   STREAM_TO_UINT8(num_resp, p);
 
-  if (inq_res_mode == BTM_INQ_RESULT_EXTENDED && (num_resp > 1)) {
-    BTM_TRACE_ERROR("btm_process_inq_results() extended results (%d) > 1",
-                    num_resp);
-    return;
+  if (inq_res_mode == BTM_INQ_RESULT_EXTENDED) {
+    if (num_resp > 1) {
+      BTM_TRACE_ERROR("btm_process_inq_results() extended results (%d) > 1",
+                      num_resp);
+      return;
+    }
+
+    constexpr uint16_t extended_inquiry_result_size = 254;
+    if (hci_evt_len - 1 != extended_inquiry_result_size) {
+      android_errorWriteLog(0x534e4554, "141620271");
+      BTM_TRACE_ERROR("%s: can't fit %d results in %d bytes", __func__,
+                      num_resp, hci_evt_len);
+      return;
+    }
+  } else if (inq_res_mode == BTM_INQ_RESULT_STANDARD ||
+             inq_res_mode == BTM_INQ_RESULT_WITH_RSSI) {
+    constexpr uint16_t inquiry_result_size = 14;
+    if (hci_evt_len < num_resp * inquiry_result_size) {
+      android_errorWriteLog(0x534e4554, "141620271");
+      BTM_TRACE_ERROR("%s: can't fit %d results in %d bytes", __func__,
+                      num_resp, hci_evt_len);
+      return;
+    }
   }
 
   for (xx = 0; xx < num_resp; xx++) {
@@ -1803,7 +1666,7 @@
       if (p_i->inq_count != p_inq->inq_counter)
         p_inq->inq_cmpl_info.num_resp++; /* A new response was found */
 
-      p_cur->inq_result_type = BTM_INQ_RESULT_BR;
+      p_cur->inq_result_type |= BTM_INQ_RESULT_BR;
       if (p_i->inq_count != p_inq->inq_counter) {
         p_cur->device_type = BT_DEVICE_TYPE_BREDR;
         p_i->scan_rsp = false;
@@ -1909,13 +1772,6 @@
 
   p_inq->inqparms.mode &= ~(mode);
 
-  if (p_inq->scan_type == INQ_LE_OBSERVE && !p_inq->inq_active) {
-    /*end of LE observe*/
-    p_inq->p_inq_ble_results_cb = NULL;
-    p_inq->p_inq_ble_cmpl_cb = NULL;
-    p_inq->scan_type = INQ_NONE;
-  }
-
 #if (BTM_INQ_DEBUG == TRUE)
   BTM_TRACE_DEBUG(
       "btm_process_inq_complete inq_active:0x%x state:%d inqfilt_active:%d",
@@ -1962,12 +1818,6 @@
       p_inq->scan_type == INQ_GENERAL)  // this inquiry is complete
   {
     p_inq->scan_type = INQ_NONE;
-    /* check if the LE observe is pending */
-    if (p_inq->p_inq_ble_results_cb != NULL) {
-      BTM_TRACE_DEBUG("BTM Inq Compl: resuming a pending LE scan");
-      BTM_BleObserve(1, 0, p_inq->p_inq_ble_results_cb,
-                     p_inq->p_inq_ble_cmpl_cb);
-    }
   }
 #if (BTM_INQ_DEBUG == TRUE)
   BTM_TRACE_DEBUG("inq_active:0x%x state:%d inqfilt_active:%d",
@@ -2041,7 +1891,7 @@
 
       /* If the database entry exists for the device, use its clock offset */
       tINQ_DB_ENT* p_i = btm_inq_db_find(remote_bda);
-      if (p_i) {
+      if (p_i && (p_i->inq_info.results.inq_result_type & BTM_INQ_RESULT_BR)) {
         tBTM_INQ_INFO* p_cur = &p_i->inq_info;
         btsnd_hcic_rmt_name_req(
             remote_bda, p_cur->results.page_scan_rep_mode,
@@ -2133,22 +1983,22 @@
 }
 
 void btm_inq_remote_name_timer_timeout(UNUSED_ATTR void* data) {
-  btm_inq_rmt_name_failed();
+  btm_inq_rmt_name_failed_cancelled();
 }
 
 /*******************************************************************************
  *
- * Function         btm_inq_rmt_name_failed
+ * Function         btm_inq_rmt_name_failed_cancelled
  *
- * Description      This function is if timeout expires while getting remote
- *                  name.  This is done for devices that incorrectly do not
- *                  report operation failure
+ * Description      This function is if timeout expires or request is cancelled
+ *                  while getting remote name.  This is done for devices that
+ *                  incorrectly do not report operation failure
  *
  * Returns          void
  *
  ******************************************************************************/
-void btm_inq_rmt_name_failed(void) {
-  BTM_TRACE_ERROR("btm_inq_rmt_name_failed()  remname_active=%d",
+void btm_inq_rmt_name_failed_cancelled(void) {
+  BTM_TRACE_ERROR("btm_inq_rmt_name_failed_cancelled()  remname_active=%d",
                   btm_cb.btm_inq_vars.remname_active);
 
   if (btm_cb.btm_inq_vars.remname_active)
diff --git a/stack/btm/btm_int.h b/stack/btm/btm_int.h
index 6b80717..05180db 100644
--- a/stack/btm/btm_int.h
+++ b/stack/btm/btm_int.h
@@ -59,13 +59,14 @@
 
 extern void btm_process_remote_name(const RawAddress* bda, BD_NAME name,
                                     uint16_t evt_len, uint8_t hci_status);
-extern void btm_inq_rmt_name_failed(void);
+extern void btm_inq_rmt_name_failed_cancelled(void);
 extern void btm_inq_remote_name_timer_timeout(void* data);
 
 /* Inquiry related functions */
 extern void btm_clr_inq_db(const RawAddress* p_bda);
 extern void btm_inq_db_init(void);
-extern void btm_process_inq_results(uint8_t* p, uint8_t inq_res_mode);
+extern void btm_process_inq_results(uint8_t* p, uint8_t hci_evt_len,
+                                    uint8_t inq_res_mode);
 extern void btm_process_inq_complete(uint8_t status, uint8_t mode);
 extern void btm_process_cancel_complete(uint8_t status, uint8_t mode);
 extern void btm_event_filter_complete(uint8_t* p);
@@ -119,7 +120,7 @@
 extern tBTM_STATUS btm_remove_acl(const RawAddress& bd_addr,
                                   tBT_TRANSPORT transport);
 extern void btm_read_remote_features_complete(uint8_t* p);
-extern void btm_read_remote_ext_features_complete(uint8_t* p);
+extern void btm_read_remote_ext_features_complete(uint8_t* p, uint8_t evt_len);
 extern void btm_read_remote_ext_features_failed(uint8_t status,
                                                 uint16_t handle);
 extern void btm_read_remote_version_complete(uint8_t* p);
diff --git a/stack/btm/btm_int_types.h b/stack/btm/btm_int_types.h
index a0460b1..1cd6cfa 100644
--- a/stack/btm/btm_int_types.h
+++ b/stack/btm/btm_int_types.h
@@ -223,7 +223,7 @@
   bool scan_rsp;
 } tINQ_DB_ENT;
 
-enum { INQ_NONE, INQ_LE_OBSERVE, INQ_GENERAL };
+enum { INQ_NONE, INQ_GENERAL };
 typedef uint8_t tBTM_INQ_TYPE;
 
 typedef struct {
@@ -252,10 +252,6 @@
 
   tBTM_CMPL_CB* p_inq_cmpl_cb;
   tBTM_INQ_RESULTS_CB* p_inq_results_cb;
-  tBTM_CMPL_CB*
-      p_inq_ble_cmpl_cb; /*completion callback exclusively for LE Observe*/
-  tBTM_INQ_RESULTS_CB*
-      p_inq_ble_results_cb; /*results callback exclusively for LE observe*/
   tBTM_CMPL_CB* p_inqfilter_cmpl_cb; /* Called (if not NULL) after inquiry
                                         filter completed */
   uint32_t inq_counter; /* Counter incremented each time an inquiry completes */
diff --git a/stack/btm/btm_sco.cc b/stack/btm/btm_sco.cc
index d8d2def..b8e0c9a 100644
--- a/stack/btm/btm_sco.cc
+++ b/stack/btm/btm_sco.cc
@@ -1016,152 +1016,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetScoPacketTypes
- *
- * Description      This function is called to set the packet types used for
- *                  a specific SCO connection,
- *
- * Parameters       pkt_types - One or more of the following
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV5
- *
- *                  BTM_SCO_LINK_ALL_MASK   - enables all supported types
- *
- * Returns          status of the operation
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetScoPacketTypes(uint16_t sco_inx, uint16_t pkt_types) {
-#if (BTM_MAX_SCO_LINKS > 0)
-  tBTM_CHG_ESCO_PARAMS parms;
-  tSCO_CONN* p;
-
-  /* Validity check */
-  if (sco_inx >= BTM_MAX_SCO_LINKS) return (BTM_UNKNOWN_ADDR);
-
-  p = &btm_cb.sco_cb.sco_db[sco_inx];
-  parms.packet_types = pkt_types;
-
-  /* Keep the other parameters the same for SCO */
-  parms.max_latency_ms = p->esco.setup.max_latency_ms;
-  parms.retransmission_effort = p->esco.setup.retransmission_effort;
-
-  return (BTM_ChangeEScoLinkParms(sco_inx, &parms));
-#else
-  return (BTM_UNKNOWN_ADDR);
-#endif
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoPacketTypes
- *
- * Description      This function is read the packet types used for a specific
- *                  SCO connection.
- *
- * Returns          Packet types supported for the connection
- *                  One or more of the following (bitmask):
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV5
- *
- ******************************************************************************/
-uint16_t BTM_ReadScoPacketTypes(uint16_t sco_inx) {
-#if (BTM_MAX_SCO_LINKS > 0)
-  tSCO_CONN* p = &btm_cb.sco_cb.sco_db[sco_inx];
-
-  /* Validity check */
-  if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED))
-    return (p->esco.setup.packet_types);
-  else
-    return (0);
-#else
-  return (0);
-#endif
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoDiscReason
- *
- * Description      This function is returns the reason why an (e)SCO connection
- *                  has been removed. It contains the value until read, or until
- *                  another (e)SCO connection has disconnected.
- *
- * Returns          HCI reason or BTM_INVALID_SCO_DISC_REASON if not set.
- *
- ******************************************************************************/
-uint16_t BTM_ReadScoDiscReason(void) {
-  uint16_t res = btm_cb.sco_cb.sco_disc_reason;
-  btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON;
-  return (res);
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadDeviceScoPacketTypes
- *
- * Description      This function is read the SCO packet types that
- *                  the device supports.
- *
- * Returns          Packet types supported by the device.
- *                  One or more of the following (bitmask):
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV5
- *
- ******************************************************************************/
-uint16_t BTM_ReadDeviceScoPacketTypes(void) {
-  return (btm_cb.btm_sco_pkt_types_supported);
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoHandle
- *
- * Description      This function is used to read the HCI handle used for a
- *                  specific SCO connection,
- *
- * Returns          handle for the connection, or 0xFFFF if invalid SCO index.
- *
- ******************************************************************************/
-uint16_t BTM_ReadScoHandle(uint16_t sco_inx) {
-#if (BTM_MAX_SCO_LINKS > 0)
-  tSCO_CONN* p = &btm_cb.sco_cb.sco_db[sco_inx];
-
-  /* Validity check */
-  if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED))
-    return (p->hci_handle);
-  else
-    return (BTM_INVALID_HCI_HANDLE);
-#else
-  return (BTM_INVALID_HCI_HANDLE);
-#endif
-}
-
-/*******************************************************************************
- *
  * Function         BTM_ReadScoBdAddr
  *
  * Description      This function is read the remote BD Address for a specific
@@ -1260,52 +1114,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadEScoLinkParms
- *
- * Description      This function returns the current eSCO link parameters for
- *                  the specified handle.  This can be called anytime a
- *                  connection is active, but is typically called after
- *                  receiving the SCO opened callback.
- *
- *                  Note: If called over a 1.1 controller, only the packet types
- *                        field has meaning.
- *
- * Returns          BTM_SUCCESS if returned data is valid connection.
- *                  BTM_WRONG_MODE if no connection with a peer device or bad
- *                                 sco_inx.
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ReadEScoLinkParms(uint16_t sco_inx, tBTM_ESCO_DATA* p_parms) {
-#if (BTM_MAX_SCO_LINKS > 0)
-  uint8_t index;
-
-  BTM_TRACE_API("%s: -> sco_inx 0x%04x", __func__, sco_inx);
-
-  if (sco_inx < BTM_MAX_SCO_LINKS &&
-      btm_cb.sco_cb.sco_db[sco_inx].state >= SCO_ST_CONNECTED) {
-    *p_parms = btm_cb.sco_cb.sco_db[sco_inx].esco.data;
-    return (BTM_SUCCESS);
-  }
-
-  if (sco_inx == BTM_FIRST_ACTIVE_SCO_INDEX) {
-    for (index = 0; index < BTM_MAX_SCO_LINKS; index++) {
-      if (btm_cb.sco_cb.sco_db[index].state >= SCO_ST_CONNECTED) {
-        BTM_TRACE_API("%s: the first active SCO index is %d", __func__, index);
-        *p_parms = btm_cb.sco_cb.sco_db[index].esco.data;
-        return (BTM_SUCCESS);
-      }
-    }
-  }
-
-#endif
-
-  BTM_TRACE_API("BTM_ReadEScoLinkParms cannot find the SCO index!");
-  memset(p_parms, 0, sizeof(tBTM_ESCO_DATA));
-  return (BTM_WRONG_MODE);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_ChangeEScoLinkParms
  *
  * Description      This function requests renegotiation of the parameters on
diff --git a/stack/btm/btm_sec.cc b/stack/btm/btm_sec.cc
index 935903d..0eeb843 100644
--- a/stack/btm/btm_sec.cc
+++ b/stack/btm/btm_sec.cc
@@ -34,6 +34,8 @@
 #include "common/metrics.h"
 #include "common/time_util.h"
 #include "device/include/controller.h"
+#include "main/shim/btm_api.h"
+#include "main/shim/shim.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 
@@ -94,15 +96,8 @@
 static tBTM_STATUS btm_sec_send_hci_disconnect(tBTM_SEC_DEV_REC* p_dev_rec,
                                                uint8_t reason,
                                                uint16_t conn_handle);
-uint8_t btm_sec_start_role_switch(tBTM_SEC_DEV_REC* p_dev_rec);
 tBTM_SEC_DEV_REC* btm_sec_find_dev_by_sec_state(uint8_t state);
 
-static bool btm_sec_set_security_level(CONNECTION_TYPE conn_type,
-                                       const char* p_name, uint8_t service_id,
-                                       uint16_t sec_level, uint16_t psm,
-                                       uint32_t mx_proto_id,
-                                       uint32_t mx_chan_id);
-
 static bool btm_dev_authenticated(tBTM_SEC_DEV_REC* p_dev_rec);
 static bool btm_dev_encrypted(tBTM_SEC_DEV_REC* p_dev_rec);
 static bool btm_dev_authorized(tBTM_SEC_DEV_REC* p_dev_rec);
@@ -232,10 +227,13 @@
  *
  ******************************************************************************/
 bool BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecRegister(p_cb_info);
+  }
 
   BTM_TRACE_EVENT("%s application registered", __func__);
 
-  LOG_INFO(LOG_TAG, "%s p_cb_info->p_le_callback == 0x%p", __func__,
+  LOG_INFO("%s p_cb_info->p_le_callback == 0x%p", __func__,
            p_cb_info->p_le_callback);
   if (p_cb_info->p_le_callback) {
     BTM_TRACE_EVENT("%s SMP_Register( btm_proc_smp_cback )", __func__);
@@ -246,11 +244,11 @@
       btm_ble_reset_id();
     }
   } else {
-    LOG_WARN(LOG_TAG, "%s p_cb_info->p_le_callback == NULL", __func__);
+    LOG_WARN("%s p_cb_info->p_le_callback == NULL", __func__);
   }
 
   btm_cb.api = *p_cb_info;
-  LOG_INFO(LOG_TAG, "%s btm_cb.api.p_le_callback = 0x%p ", __func__,
+  LOG_INFO("%s btm_cb.api.p_le_callback = 0x%p ", __func__,
            btm_cb.api.p_le_callback);
   BTM_TRACE_EVENT("%s application registered", __func__);
   return (true);
@@ -258,23 +256,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecRegisterLinkKeyNotificationCallback
- *
- * Description      Application manager calls this function to register for
- *                  link key notification.  When there is nobody registered
- *                  we should avoid changing link key
- *
- * Returns          true if registered OK, else false
- *
- ******************************************************************************/
-bool BTM_SecRegisterLinkKeyNotificationCallback(
-    tBTM_LINK_KEY_CALLBACK* p_callback) {
-  btm_cb.api.p_link_key_callback = p_callback;
-  return true;
-}
-
-/*******************************************************************************
- *
  * Function         BTM_SecAddRmtNameNotifyCallback
  *
  * Description      Any profile can register to be notified when name of the
@@ -415,28 +396,6 @@
   btm_cb.connect_only_paired = connect_only_paired;
 }
 
-/*******************************************************************************
- *
- * Function         BTM_SetSecureConnectionsOnly
- *
- * Description      Enable or disable default treatment for Mode 4 Level 0
- *                  services
- *
- * Parameter        secure_connections_only_mode -
- *                  true means that the device should treat Mode 4 Level 0
- *                       services as services of other levels.
- *                  false means that the device should provide default
- *                        treatment for Mode 4 Level 0 services.
- *
- * Returns          void
- *
- ******************************************************************************/
-void BTM_SetSecureConnectionsOnly(bool secure_connections_only_mode) {
-  BTM_TRACE_API("%s: Mode : %u", __func__, secure_connections_only_mode);
-
-  btm_cb.devcb.secure_connections_only = secure_connections_only_mode;
-  btm_cb.security_mode = BTM_SEC_MODE_SC;
-}
 #define BTM_NO_AVAIL_SEC_SERVICES ((uint16_t)0xffff)
 
 /*******************************************************************************
@@ -462,41 +421,10 @@
 bool BTM_SetSecurityLevel(bool is_originator, const char* p_name,
                           uint8_t service_id, uint16_t sec_level, uint16_t psm,
                           uint32_t mx_proto_id, uint32_t mx_chan_id) {
-  return (btm_sec_set_security_level(is_originator, p_name, service_id,
-                                     sec_level, psm, mx_proto_id, mx_chan_id));
-}
-
-/*******************************************************************************
- *
- * Function         btm_sec_set_security_level
- *
- * Description      Register service security level with Security Manager
- *
- * Parameters:      conn_type   - true if originating the connection
- *                  p_name      - Name of the service relevant only if
- *                                authorization will show this name to user.
- *                                Ignored if BTM_SEC_SERVICE_NAME_LEN is 0.
- *                  service_id  - service ID for the service passed to
- *                                authorization callback
- *                  sec_level   - bit mask of the security features
- *                  psm         - L2CAP PSM
- *                  mx_proto_id - protocol ID of multiplexing proto below
- *                  mx_chan_id  - channel ID of multiplexing proto below
- *
- * Returns          true if registered OK, else false
- *
- ******************************************************************************/
-static bool btm_sec_set_security_level(CONNECTION_TYPE conn_type,
-                                       const char* p_name, uint8_t service_id,
-                                       uint16_t sec_level, uint16_t psm,
-                                       uint32_t mx_proto_id,
-                                       uint32_t mx_chan_id) {
   tBTM_SEC_SERV_REC* p_srec;
   uint16_t index;
   uint16_t first_unused_record = BTM_NO_AVAIL_SEC_SERVICES;
   bool record_allocated = false;
-  bool is_originator;
-  is_originator = conn_type;
 
   BTM_TRACE_API("%s : sec: 0x%x", __func__, sec_level);
 
@@ -1034,7 +962,7 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecBondByTransport
+ * Function         BTM_SecBond
  *
  * Description      This function is called to perform bonding with peer device.
  *                  If the connection is already up, but not secure, pairing
@@ -1049,11 +977,19 @@
  *
  *  Note: After 2.1 parameters are not used and preserved here not to change API
  ******************************************************************************/
-tBTM_STATUS BTM_SecBondByTransport(const RawAddress& bd_addr,
-                                   tBT_TRANSPORT transport, uint8_t pin_len,
-                                   uint8_t* p_pin, uint32_t trusted_mask[]) {
+tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                        tBT_TRANSPORT transport, int device_type,
+                        uint8_t pin_len, uint8_t* p_pin,
+                        uint32_t trusted_mask[]) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecBond(bd_addr, addr_type, transport,
+                                        device_type);
+  }
+
+  if (transport == BT_TRANSPORT_INVALID)
+    transport = BTM_UseLeLink(bd_addr) ? BT_TRANSPORT_LE : BT_TRANSPORT_BR_EDR;
+
   tBT_DEVICE_TYPE dev_type;
-  tBLE_ADDR_TYPE addr_type;
 
   BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type);
   /* LE device, do SMP pairing */
@@ -1068,29 +1004,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecBond
- *
- * Description      This function is called to perform bonding with peer device.
- *                  If the connection is already up, but not secure, pairing
- *                  is attempted.  If already paired BTM_SUCCESS is returned.
- *
- * Parameters:      bd_addr      - Address of the device to bond
- *                  pin_len      - length in bytes of the PIN Code
- *                  p_pin        - pointer to array with the PIN Code
- *                  trusted_mask - bitwise OR of trusted services
- *                                 (array of uint32_t)
- *
- *  Note: After 2.1 parameters are not used and preserved here not to change API
- ******************************************************************************/
-tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, uint8_t pin_len,
-                        uint8_t* p_pin, uint32_t trusted_mask[]) {
-  tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
-  if (BTM_UseLeLink(bd_addr)) transport = BT_TRANSPORT_LE;
-  return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin,
-                                   trusted_mask);
-}
-/*******************************************************************************
- *
  * Function         BTM_SecBondCancel
  *
  * Description      This function is called to cancel ongoing bonding process
@@ -1101,6 +1014,10 @@
  *
  ******************************************************************************/
 tBTM_STATUS BTM_SecBondCancel(const RawAddress& bd_addr) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecBondCancel(bd_addr);
+  }
+
   tBTM_SEC_DEV_REC* p_dev_rec;
 
   BTM_TRACE_API("BTM_SecBondCancel()  State: %s flags:0x%x",
@@ -1169,30 +1086,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecGetDeviceLinkKey
- *
- * Description      This function is called to obtain link key for the device
- *                  it returns BTM_SUCCESS if link key is available, or
- *                  BTM_UNKNOWN_ADDR if Security Manager does not know about
- *                  the device or device record does not contain link key info
- *
- * Parameters:      bd_addr      - Address of the device
- *                  link_key     - Link Key is copied into this pointer
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SecGetDeviceLinkKey(const RawAddress& bd_addr,
-                                    LinkKey* link_key) {
-  tBTM_SEC_DEV_REC* p_dev_rec;
-  p_dev_rec = btm_find_dev(bd_addr);
-  if ((p_dev_rec != NULL) && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) {
-    *link_key = p_dev_rec->link_key;
-    return (BTM_SUCCESS);
-  }
-  return (BTM_UNKNOWN_ADDR);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_SecGetDeviceLinkKeyType
  *
  * Description      This function is called to obtain link key type for the
@@ -1588,90 +1481,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_BuildOobData
- *
- * Description      This function is called to build the OOB data payload to
- *                  be sent over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  max_len - p_data size.
- *                  c       - simple pairing Hash C.
- *                  r       - simple pairing Randomizer  C.
- *                  name_len- 0, local device name would not be included.
- *                            otherwise, the local device name is included for
- *                            up to this specified length
- *
- * Returns          Number of bytes in p_data.
- *
- ******************************************************************************/
-uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len, const Octet16& c,
-                          const Octet16& r, uint8_t name_len) {
-  uint8_t* p = p_data;
-  uint16_t len = 0;
-  uint16_t name_size;
-  uint8_t name_type = BTM_EIR_SHORTENED_LOCAL_NAME_TYPE;
-
-  if (p_data && max_len >= BTM_OOB_MANDATORY_SIZE) {
-    /* add mandatory part */
-    UINT16_TO_STREAM(p, len);
-    BDADDR_TO_STREAM(p, *controller_get_interface()->get_address());
-
-    len = BTM_OOB_MANDATORY_SIZE;
-    max_len -= len;
-
-    /* now optional part */
-
-    /* add Hash C */
-    uint16_t delta = BTM_OOB_HASH_C_SIZE + 2;
-    if (max_len >= delta) {
-      *p++ = BTM_OOB_HASH_C_SIZE + 1;
-      *p++ = BTM_EIR_OOB_SSP_HASH_C_TYPE;
-      ARRAY_TO_STREAM(p, c.data(), BTM_OOB_HASH_C_SIZE);
-      len += delta;
-      max_len -= delta;
-    }
-
-    /* add Rand R */
-    delta = BTM_OOB_RAND_R_SIZE + 2;
-    if (max_len >= delta) {
-      *p++ = BTM_OOB_RAND_R_SIZE + 1;
-      *p++ = BTM_EIR_OOB_SSP_RAND_R_TYPE;
-      ARRAY_TO_STREAM(p, r.data(), BTM_OOB_RAND_R_SIZE);
-      len += delta;
-      max_len -= delta;
-    }
-
-    /* add class of device */
-    delta = BTM_OOB_COD_SIZE + 2;
-    if (max_len >= delta) {
-      *p++ = BTM_OOB_COD_SIZE + 1;
-      *p++ = BTM_EIR_OOB_COD_TYPE;
-      DEVCLASS_TO_STREAM(p, btm_cb.devcb.dev_class);
-      len += delta;
-      max_len -= delta;
-    }
-    name_size = name_len;
-    if (name_size > strlen(btm_cb.cfg.bd_name)) {
-      name_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE;
-      name_size = (uint16_t)strlen(btm_cb.cfg.bd_name);
-    }
-    delta = name_size + 2;
-    if (max_len >= delta) {
-      *p++ = name_size + 1;
-      *p++ = name_type;
-      ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, name_size);
-      len += delta;
-      max_len -= delta;
-    }
-    /* update len */
-    p = p_data;
-    UINT16_TO_STREAM(p, len);
-  }
-  return len;
-}
-
-/*******************************************************************************
- *
  * Function         BTM_BothEndsSupportSecureConnections
  *
  * Description      This function is called to check if both the local device
@@ -1716,64 +1525,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadOobData
- *
- * Description      This function is called to parse the OOB data payload
- *                  received over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  eir_tag - The associated EIR tag to read the data.
- *                  *p_len(output) - the length of the data with the given tag.
- *
- * Returns          the beginning of the data with the given tag.
- *                  NULL, if the tag is not found.
- *
- ******************************************************************************/
-uint8_t* BTM_ReadOobData(uint8_t* p_data, uint8_t eir_tag, uint8_t* p_len) {
-  uint8_t* p = p_data;
-  uint16_t max_len;
-  uint8_t len, type;
-  uint8_t* p_ret = NULL;
-  uint8_t ret_len = 0;
-
-  if (p_data) {
-    STREAM_TO_UINT16(max_len, p);
-    if (max_len >= BTM_OOB_MANDATORY_SIZE) {
-      if (BTM_EIR_OOB_BD_ADDR_TYPE == eir_tag) {
-        p_ret = p; /* the location for bd_addr */
-        ret_len = BTM_OOB_BD_ADDR_SIZE;
-      } else {
-        p += BD_ADDR_LEN;
-        max_len -= BTM_OOB_MANDATORY_SIZE;
-        /* now the optional data in EIR format */
-        while (max_len > 0) {
-          len = *p++; /* tag data len + 1 */
-          type = *p++;
-          if (eir_tag == type) {
-            p_ret = p;
-            ret_len = len - 1;
-            break;
-          }
-          /* the data size of this tag is len + 1 (tag data len + 2) */
-          if (max_len > len) {
-            max_len -= len;
-            max_len--;
-            len--;
-            p += len;
-          } else
-            max_len = 0;
-        }
-      }
-    }
-  }
-
-  if (p_len) *p_len = ret_len;
-
-  return p_ret;
-}
-
-/*******************************************************************************
- *
  * Function         BTM_SetOutService
  *
  * Description      This function is called to set the service for
@@ -1941,13 +1692,11 @@
   bool old_is_originator;
   tBTM_STATUS rc = BTM_SUCCESS;
   bool chk_acp_auth_done = false;
-  bool is_originator;
-  tBT_TRANSPORT transport =
+  const bool is_originator = conn_type;
+  constexpr tBT_TRANSPORT transport =
       BT_TRANSPORT_BR_EDR; /* should check PSM range in LE connection oriented
                               L2CAP connection */
 
-  is_originator = conn_type;
-
   BTM_TRACE_DEBUG("%s() is_originator:%d, 0x%x, psm=0x%04x", __func__,
                   is_originator, p_ref_data, psm);
 
@@ -2186,7 +1935,7 @@
       because of data path issues. Delay this disconnect a little bit
       */
       LOG_INFO(
-          LOG_TAG,
+
           "%s peer should have initiated security process by now (SM4 to SM4)",
           __func__);
       p_dev_rec->p_callback = p_callback;
@@ -4483,8 +4232,8 @@
   p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
 
 #if (BTM_DISC_DURING_RS == TRUE)
-  LOG_INFO(LOG_TAG, "%s clearing pending flag handle:%d reason:%d", __func__,
-           handle, reason);
+  LOG_INFO("%s clearing pending flag handle:%d reason:%d", __func__, handle,
+           reason);
   p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
 #endif
 
diff --git a/stack/btu/btu_hcif.cc b/stack/btu/btu_hcif.cc
index 2035cc6..57a4e9e 100644
--- a/stack/btu/btu_hcif.cc
+++ b/stack/btu/btu_hcif.cc
@@ -43,11 +43,13 @@
 #include "bt_common.h"
 #include "bt_types.h"
 #include "bt_utils.h"
+#include "btif_config.h"
 #include "btm_api.h"
 #include "btm_int.h"
 #include "btu.h"
 #include "common/metrics.h"
 #include "device/include/controller.h"
+#include "hci_evt_length.h"
 #include "hci_layer.h"
 #include "hcimsgs.h"
 #include "l2c_int.h"
@@ -65,18 +67,20 @@
 /*            L O C A L    F U N C T I O N     P R O T O T Y P E S            */
 /******************************************************************************/
 static void btu_hcif_inquiry_comp_evt(uint8_t* p);
-static void btu_hcif_inquiry_result_evt(uint8_t* p);
-static void btu_hcif_inquiry_rssi_result_evt(uint8_t* p);
-static void btu_hcif_extended_inquiry_result_evt(uint8_t* p);
+static void btu_hcif_inquiry_result_evt(uint8_t* p, uint8_t hci_evt_len);
+static void btu_hcif_inquiry_rssi_result_evt(uint8_t* p, uint8_t hci_evt_len);
+static void btu_hcif_extended_inquiry_result_evt(uint8_t* p,
+                                                 uint8_t hci_evt_len);
 
-static void btu_hcif_connection_comp_evt(uint8_t* p);
+static void btu_hcif_connection_comp_evt(uint8_t* p, uint8_t evt_len);
 static void btu_hcif_connection_request_evt(uint8_t* p);
 static void btu_hcif_disconnection_comp_evt(uint8_t* p);
 static void btu_hcif_authentication_comp_evt(uint8_t* p);
 static void btu_hcif_rmt_name_request_comp_evt(uint8_t* p, uint16_t evt_len);
 static void btu_hcif_encryption_change_evt(uint8_t* p);
 static void btu_hcif_read_rmt_features_comp_evt(uint8_t* p);
-static void btu_hcif_read_rmt_ext_features_comp_evt(uint8_t* p);
+static void btu_hcif_read_rmt_ext_features_comp_evt(uint8_t* p,
+                                                    uint8_t evt_len);
 static void btu_hcif_read_rmt_version_comp_evt(uint8_t* p);
 static void btu_hcif_qos_setup_comp_evt(uint8_t* p);
 static void btu_hcif_command_complete_evt(BT_HDR* response, void* context);
@@ -85,7 +89,7 @@
 static void btu_hcif_hardware_error_evt(uint8_t* p);
 static void btu_hcif_flush_occured_evt(void);
 static void btu_hcif_role_change_evt(uint8_t* p);
-static void btu_hcif_num_compl_data_pkts_evt(uint8_t* p);
+static void btu_hcif_num_compl_data_pkts_evt(uint8_t* p, uint8_t evt_len);
 static void btu_hcif_mode_change_evt(uint8_t* p);
 static void btu_hcif_pin_code_request_evt(uint8_t* p);
 static void btu_hcif_link_key_request_evt(uint8_t* p);
@@ -189,6 +193,44 @@
       bluetooth::common::LogLinkLayerConnectionEvent(
           &bda, handle, android::bluetooth::DIRECTION_UNKNOWN, link_type, cmd,
           evt_code, android::bluetooth::hci::BLE_EVT_UNKNOWN, status, reason);
+
+      // Read SDP_DI manufacturer, model, HW version from config,
+      // and log them
+      int sdp_di_manufacturer_id = 0;
+      int sdp_di_model_id = 0;
+      int sdp_di_hw_version = 0;
+      int sdp_di_vendor_id_source = 0;
+      std::string bda_string = bda.ToString();
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_SDP_DI_MANUFACTURER,
+                          &sdp_di_manufacturer_id);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_SDP_DI_MODEL,
+                          &sdp_di_model_id);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_SDP_DI_HW_VERSION,
+                          &sdp_di_hw_version);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_SDP_DI_VENDOR_ID_SRC,
+                          &sdp_di_vendor_id_source);
+
+      std::stringstream ss;
+      // [N - native]::SDP::[DIP - Device ID Profile]
+      ss << "N:SDP::DIP::" << loghex(sdp_di_vendor_id_source);
+      bluetooth::common::LogManufacturerInfo(
+          bda, android::bluetooth::DeviceInfoSrcEnum::DEVICE_INFO_INTERNAL,
+          ss.str(), loghex(sdp_di_manufacturer_id), loghex(sdp_di_model_id),
+          loghex(sdp_di_hw_version), "");
+
+      // Read LMP version, subversion and  manufacturer from config,
+      // and log them
+      int lmp_version = -1;
+      int lmp_subversion = -1;
+      int lmp_manufacturer_id = -1;
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_REMOTE_VER_VER,
+                          &lmp_version);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_REMOTE_VER_SUBVER,
+                          &lmp_subversion);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_REMOTE_VER_MFCT,
+                          &lmp_manufacturer_id);
+      bluetooth::common::LogRemoteVersionInfo(
+          handle, status, lmp_version, lmp_manufacturer_id, lmp_subversion);
       break;
     }
     case HCI_CONNECTION_REQUEST_EVT: {
@@ -256,6 +298,13 @@
   STREAM_TO_UINT8(hci_evt_code, p);
   STREAM_TO_UINT8(hci_evt_len, p);
 
+  // validate event size
+  if (hci_evt_len < hci_event_parameters_minimum_length[hci_evt_code]) {
+    HCI_TRACE_WARNING("%s: evt:0x%2X, malformed event of size %hhd", __func__,
+                      hci_evt_code, hci_evt_len);
+    return;
+  }
+
   btu_hcif_log_event_metrics(hci_evt_code, p);
 
   switch (hci_evt_code) {
@@ -263,16 +312,16 @@
       btu_hcif_inquiry_comp_evt(p);
       break;
     case HCI_INQUIRY_RESULT_EVT:
-      btu_hcif_inquiry_result_evt(p);
+      btu_hcif_inquiry_result_evt(p, hci_evt_len);
       break;
     case HCI_INQUIRY_RSSI_RESULT_EVT:
-      btu_hcif_inquiry_rssi_result_evt(p);
+      btu_hcif_inquiry_rssi_result_evt(p, hci_evt_len);
       break;
     case HCI_EXTENDED_INQUIRY_RESULT_EVT:
-      btu_hcif_extended_inquiry_result_evt(p);
+      btu_hcif_extended_inquiry_result_evt(p, hci_evt_len);
       break;
     case HCI_CONNECTION_COMP_EVT:
-      btu_hcif_connection_comp_evt(p);
+      btu_hcif_connection_comp_evt(p, hci_evt_len);
       break;
     case HCI_CONNECTION_REQUEST_EVT:
       btu_hcif_connection_request_evt(p);
@@ -296,7 +345,7 @@
       btu_hcif_read_rmt_features_comp_evt(p);
       break;
     case HCI_READ_RMT_EXT_FEATURES_COMP_EVT:
-      btu_hcif_read_rmt_ext_features_comp_evt(p);
+      btu_hcif_read_rmt_ext_features_comp_evt(p, hci_evt_len);
       break;
     case HCI_READ_RMT_VERSION_COMP_EVT:
       btu_hcif_read_rmt_version_comp_evt(p);
@@ -305,16 +354,16 @@
       btu_hcif_qos_setup_comp_evt(p);
       break;
     case HCI_COMMAND_COMPLETE_EVT:
-      LOG_ERROR(LOG_TAG,
-                "%s should not have received a command complete event. "
-                "Someone didn't go through the hci transmit_command function.",
-                __func__);
+      LOG_ERROR(
+          "%s should not have received a command complete event. "
+          "Someone didn't go through the hci transmit_command function.",
+          __func__);
       break;
     case HCI_COMMAND_STATUS_EVT:
-      LOG_ERROR(LOG_TAG,
-                "%s should not have received a command status event. "
-                "Someone didn't go through the hci transmit_command function.",
-                __func__);
+      LOG_ERROR(
+          "%s should not have received a command status event. "
+          "Someone didn't go through the hci transmit_command function.",
+          __func__);
       break;
     case HCI_HARDWARE_ERROR_EVT:
       btu_hcif_hardware_error_evt(p);
@@ -326,7 +375,7 @@
       btu_hcif_role_change_evt(p);
       break;
     case HCI_NUM_COMPL_DATA_PKTS_EVT:
-      btu_hcif_num_compl_data_pkts_evt(p);
+      btu_hcif_num_compl_data_pkts_evt(p, hci_evt_len);
       break;
     case HCI_MODE_CHANGE_EVT:
       btu_hcif_mode_change_evt(p);
@@ -948,9 +997,9 @@
  * Returns          void
  *
  ******************************************************************************/
-static void btu_hcif_inquiry_result_evt(uint8_t* p) {
+static void btu_hcif_inquiry_result_evt(uint8_t* p, uint8_t hci_evt_len) {
   /* Store results in the cache */
-  btm_process_inq_results(p, BTM_INQ_RESULT_STANDARD);
+  btm_process_inq_results(p, hci_evt_len, BTM_INQ_RESULT_STANDARD);
 }
 
 /*******************************************************************************
@@ -962,9 +1011,9 @@
  * Returns          void
  *
  ******************************************************************************/
-static void btu_hcif_inquiry_rssi_result_evt(uint8_t* p) {
+static void btu_hcif_inquiry_rssi_result_evt(uint8_t* p, uint8_t hci_evt_len) {
   /* Store results in the cache */
-  btm_process_inq_results(p, BTM_INQ_RESULT_WITH_RSSI);
+  btm_process_inq_results(p, hci_evt_len, BTM_INQ_RESULT_WITH_RSSI);
 }
 
 /*******************************************************************************
@@ -976,9 +1025,10 @@
  * Returns          void
  *
  ******************************************************************************/
-static void btu_hcif_extended_inquiry_result_evt(uint8_t* p) {
+static void btu_hcif_extended_inquiry_result_evt(uint8_t* p,
+                                                 uint8_t hci_evt_len) {
   /* Store results in the cache */
-  btm_process_inq_results(p, BTM_INQ_RESULT_EXTENDED);
+  btm_process_inq_results(p, hci_evt_len, BTM_INQ_RESULT_EXTENDED);
 }
 
 /*******************************************************************************
@@ -990,7 +1040,7 @@
  * Returns          void
  *
  ******************************************************************************/
-static void btu_hcif_connection_comp_evt(uint8_t* p) {
+static void btu_hcif_connection_comp_evt(uint8_t* p, uint8_t evt_len) {
   uint8_t status;
   uint16_t handle;
   RawAddress bda;
@@ -998,6 +1048,12 @@
   uint8_t enc_mode;
   tBTM_ESCO_DATA esco_data;
 
+  if (evt_len < 11) {
+    android_errorWriteLog(0x534e4554, "141619686");
+    HCI_TRACE_WARNING("%s: malformed event of size %hhd", __func__, evt_len);
+    return;
+  }
+
   STREAM_TO_UINT8(status, p);
   STREAM_TO_UINT16(handle, p);
   STREAM_TO_BDADDR(bda, p);
@@ -1212,7 +1268,8 @@
  * Returns          void
  *
  ******************************************************************************/
-static void btu_hcif_read_rmt_ext_features_comp_evt(uint8_t* p) {
+static void btu_hcif_read_rmt_ext_features_comp_evt(uint8_t* p,
+                                                    uint8_t evt_len) {
   uint8_t* p_cur = p;
   uint8_t status;
   uint16_t handle;
@@ -1220,7 +1277,7 @@
   STREAM_TO_UINT8(status, p_cur);
 
   if (status == HCI_SUCCESS)
-    btm_read_remote_ext_features_complete(p);
+    btm_read_remote_ext_features_complete(p, evt_len);
   else {
     STREAM_TO_UINT16(handle, p_cur);
     btm_read_remote_ext_features_failed(status, handle);
@@ -1647,6 +1704,12 @@
  ******************************************************************************/
 static void btu_hcif_hardware_error_evt(uint8_t* p) {
   HCI_TRACE_ERROR("Ctlr H/w error event - code:0x%x", *p);
+  if (hci_is_root_inflammation_event_received()) {
+    // Ignore the hardware error event here as we have already received
+    // root inflammation event earlier.
+    HCI_TRACE_ERROR("H/w error event after root inflammation event!");
+    return;
+  }
 
   /* If anyone wants device status notifications, give him one. */
   btm_report_device_status(BTM_DEV_STATUS_DOWN);
@@ -1698,9 +1761,9 @@
  * Returns          void
  *
  ******************************************************************************/
-static void btu_hcif_num_compl_data_pkts_evt(uint8_t* p) {
+static void btu_hcif_num_compl_data_pkts_evt(uint8_t* p, uint8_t evt_len) {
   /* Process for L2CAP and SCO */
-  l2c_link_process_num_completed_pkts(p);
+  l2c_link_process_num_completed_pkts(p, evt_len);
 
   /* Send on to SCO */
   /*?? No SCO for now */
diff --git a/stack/btu/btu_task.cc b/stack/btu/btu_task.cc
index 8838206..06d06d1 100644
--- a/stack/btu/btu_task.cc
+++ b/stack/btu/btu_task.cc
@@ -97,6 +97,17 @@
   return BT_STATUS_SUCCESS;
 }
 
+bt_status_t do_in_main_thread_delayed(const base::Location& from_here,
+                                      base::OnceClosure task,
+                                      const base::TimeDelta& delay) {
+  if (!get_main_message_loop()->task_runner()->PostDelayedTask(
+          from_here, std::move(task), delay)) {
+    LOG(ERROR) << __func__ << ": failed from " << from_here.ToString();
+    return BT_STATUS_FAIL;
+  }
+  return BT_STATUS_SUCCESS;
+}
+
 void btu_task_start_up(UNUSED_ATTR void* context) {
   LOG(INFO) << "Bluetooth chip preload is complete";
 
diff --git a/stack/crypto_toolbox/aes_cmac.cc b/stack/crypto_toolbox/aes_cmac.cc
index 72b07bd..a5342b3 100644
--- a/stack/crypto_toolbox/aes_cmac.cc
+++ b/stack/crypto_toolbox/aes_cmac.cc
@@ -180,7 +180,8 @@
  *  length - length of the input in byte.
  */
 Octet16 aes_cmac(const Octet16& key, const uint8_t* input, uint16_t length) {
-  uint16_t len, diff;
+  uint32_t len;
+  uint16_t diff;
   /* n is number of rounds */
   uint16_t n = (length + OCTET16_LEN - 1) / OCTET16_LEN;
 
diff --git a/stack/gap/gap_conn.cc b/stack/gap/gap_conn.cc
index aecefd5..1285bc5 100644
--- a/stack/gap/gap_conn.cc
+++ b/stack/gap/gap_conn.cc
@@ -233,9 +233,23 @@
   else
     conn.reg_info.pL2CA_ConnectInd_Cb = gap_connect_ind;
 
+  /* Fill in eL2CAP parameter data */
+  if (p_ccb->cfg.fcr_present) {
+    if (ertm_info == NULL) {
+      p_ccb->ertm_info.preferred_mode = p_ccb->cfg.fcr.mode;
+      p_ccb->ertm_info.user_rx_buf_size = GAP_DATA_BUF_SIZE;
+      p_ccb->ertm_info.user_tx_buf_size = GAP_DATA_BUF_SIZE;
+      p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE;
+      p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE;
+    } else {
+      p_ccb->ertm_info = *ertm_info;
+    }
+  }
+
   /* Register the PSM with L2CAP */
   if (transport == BT_TRANSPORT_BR_EDR) {
-    p_ccb->psm = L2CA_Register(psm, &conn.reg_info, false /* enable_snoop */);
+    p_ccb->psm = L2CA_Register(psm, &conn.reg_info, false /* enable_snoop */,
+                               &p_ccb->ertm_info);
     if (p_ccb->psm == 0) {
       LOG(ERROR) << StringPrintf("%s: Failure registering PSM 0x%04x", __func__,
                                  psm);
@@ -262,19 +276,6 @@
     return (GAP_INVALID_HANDLE);
   }
 
-  /* Fill in eL2CAP parameter data */
-  if (p_ccb->cfg.fcr_present) {
-    if (ertm_info == NULL) {
-      p_ccb->ertm_info.preferred_mode = p_ccb->cfg.fcr.mode;
-      p_ccb->ertm_info.user_rx_buf_size = GAP_DATA_BUF_SIZE;
-      p_ccb->ertm_info.user_tx_buf_size = GAP_DATA_BUF_SIZE;
-      p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE;
-      p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE;
-    } else {
-      p_ccb->ertm_info = *ertm_info;
-    }
-  }
-
   /* optional FCR channel modes */
   if (ertm_info != NULL) {
     p_ccb->ertm_info.allowed_modes =
diff --git a/stack/gatt/gatt_attr.cc b/stack/gatt/gatt_attr.cc
index 1acfd81..ad559bc 100644
--- a/stack/gatt/gatt_attr.cc
+++ b/stack/gatt/gatt_attr.cc
@@ -23,6 +23,9 @@
  *
  ******************************************************************************/
 
+#include <map>
+
+#include "base/callback.h"
 #include "bt_target.h"
 #include "bt_utils.h"
 
@@ -33,16 +36,19 @@
 using base::StringPrintf;
 using bluetooth::Uuid;
 
-#define GATTP_MAX_NUM_INC_SVR 0
-#define GATTP_MAX_CHAR_NUM 2
-#define GATTP_MAX_ATTR_NUM (GATTP_MAX_CHAR_NUM * 2 + GATTP_MAX_NUM_INC_SVR + 1)
-#define GATTP_MAX_CHAR_VALUE_SIZE 50
+#define BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK 0x01
 
-#ifndef GATTP_ATTR_DB_SIZE
-#define GATTP_ATTR_DB_SIZE                                    \
-  GATT_DB_MEM_SIZE(GATTP_MAX_NUM_INC_SVR, GATTP_MAX_CHAR_NUM, \
-                   GATTP_MAX_CHAR_VALUE_SIZE)
-#endif
+#define BLE_GATT_CL_SUP_FEAT_CACHING_BITMASK 0x01
+#define BLE_GATT_CL_SUP_FEAT_EATT_BITMASK 0x02
+
+using gatt_eatt_support_cb = base::OnceCallback<void(const RawAddress&, bool)>;
+
+typedef struct {
+  uint16_t op_uuid;
+  gatt_eatt_support_cb cb;
+} gatt_op_cb_data;
+
+static std::map<uint16_t, gatt_op_cb_data> OngoingOps;
 
 static void gatt_request_cback(uint16_t conn_id, uint32_t trans_id,
                                uint8_t op_code, tGATTS_DATA* p_data);
@@ -54,10 +60,9 @@
                                 tGATT_DISC_RES* p_data);
 static void gatt_disc_cmpl_cback(uint16_t conn_id, tGATT_DISC_TYPE disc_type,
                                  tGATT_STATUS status);
-static void gatt_cl_op_cmpl_cback(UNUSED_ATTR uint16_t conn_id,
-                                  UNUSED_ATTR tGATTC_OPTYPE op,
-                                  UNUSED_ATTR tGATT_STATUS status,
-                                  UNUSED_ATTR tGATT_CL_COMPLETE* p_data);
+static void gatt_cl_op_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
+                                  tGATT_STATUS status,
+                                  tGATT_CL_COMPLETE* p_data);
 
 static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB* p_clcb);
 
@@ -182,6 +187,69 @@
   memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB));
 }
 
+/** GAP Attributes Database Request callback */
+tGATT_STATUS read_attr_value(uint16_t handle, tGATT_VALUE* p_value,
+                             bool is_long) {
+  uint8_t* p = p_value->value;
+
+  if (handle == gatt_cb.handle_sr_supported_feat) {
+    /* GATT_UUID_SERVER_SUP_FEAT*/
+    if (is_long) return GATT_NOT_LONG;
+
+    UINT8_TO_STREAM(p, gatt_cb.gatt_svr_supported_feat_mask);
+    p_value->len = sizeof(gatt_cb.gatt_svr_supported_feat_mask);
+    return GATT_SUCCESS;
+  }
+
+  if (handle == gatt_cb.handle_cl_supported_feat) {
+    /*GATT_UUID_CLIENT_SUP_FEAT */
+    if (is_long) return GATT_NOT_LONG;
+
+    /* Here we need to have value per peer device, for now we can always
+     * return 0 and wait for the peer to write it back. We actually don't
+     * care too much as we are also server, so peer knows we do support eatt.
+     */
+    UINT8_TO_STREAM(p, 0);
+    p_value->len = 1;
+    return GATT_SUCCESS;
+  }
+
+  if (handle == gatt_cb.handle_of_h_r) {
+    /* GATT_UUID_GATT_SRV_CHGD */
+    return GATT_READ_NOT_PERMIT;
+  }
+
+  return GATT_NOT_FOUND;
+}
+
+/** GAP Attributes Database Read/Read Blob Request process */
+tGATT_STATUS proc_read_req(tGATTS_REQ_TYPE, tGATT_READ_REQ* p_data,
+                           tGATTS_RSP* p_rsp) {
+  if (p_data->is_long) p_rsp->attr_value.offset = p_data->offset;
+
+  p_rsp->attr_value.handle = p_data->handle;
+
+  return read_attr_value(p_data->handle, &p_rsp->attr_value, p_data->is_long);
+}
+
+/** GAP ATT server process a write request */
+uint8_t proc_write_req(tGATTS_REQ_TYPE, tGATT_WRITE_REQ* p_data) {
+  /* GATT_UUID_SERVER_SUP_FEAT*/
+  if (p_data->handle == gatt_cb.handle_sr_supported_feat)
+    return GATT_WRITE_NOT_PERMIT;
+
+  /* GATT_UUID_CLIENT_SUP_FEAT:
+   * TODO: We should store the value here, but we don't need it for now.
+   * Just acknowledge write success.
+   */
+  if (p_data->handle == gatt_cb.handle_cl_supported_feat) return GATT_SUCCESS;
+
+  /* GATT_UUID_GATT_SRV_CHGD */
+  if (p_data->handle == gatt_cb.handle_of_h_r) return GATT_WRITE_NOT_PERMIT;
+
+  return GATT_NOT_FOUND;
+}
+
 /*******************************************************************************
  *
  * Function         gatt_request_cback
@@ -195,30 +263,28 @@
                                tGATTS_REQ_TYPE type, tGATTS_DATA* p_data) {
   uint8_t status = GATT_INVALID_PDU;
   tGATTS_RSP rsp_msg;
-  bool ignore = false;
+  bool rsp_needed = true;
 
   memset(&rsp_msg, 0, sizeof(tGATTS_RSP));
 
   switch (type) {
     case GATTS_REQ_TYPE_READ_CHARACTERISTIC:
     case GATTS_REQ_TYPE_READ_DESCRIPTOR:
-      status = GATT_READ_NOT_PERMIT;
+      status = proc_read_req(type, &p_data->read_req, &rsp_msg);
       break;
 
     case GATTS_REQ_TYPE_WRITE_CHARACTERISTIC:
     case GATTS_REQ_TYPE_WRITE_DESCRIPTOR:
-      status = GATT_WRITE_NOT_PERMIT;
-      break;
-
     case GATTS_REQ_TYPE_WRITE_EXEC:
     case GATT_CMD_WRITE:
-      ignore = true;
-      VLOG(1) << "Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD";
+      if (!p_data->write_req.need_rsp) rsp_needed = false;
+
+      status = proc_write_req(type, &p_data->write_req);
       break;
 
     case GATTS_REQ_TYPE_MTU:
       VLOG(1) << "Get MTU exchange new mtu size: " << +p_data->mtu;
-      ignore = true;
+      rsp_needed = false;
       break;
 
     default:
@@ -226,7 +292,7 @@
       break;
   }
 
-  if (!ignore) GATTS_SendRsp(conn_id, trans_id, status, &rsp_msg);
+  if (rsp_needed) GATTS_SendRsp(conn_id, trans_id, status, &rsp_msg);
 }
 
 /*******************************************************************************
@@ -282,7 +348,9 @@
 
   Uuid service_uuid = Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER);
 
-  Uuid char_uuid = Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD);
+  Uuid srv_changed_char_uuid = Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD);
+  Uuid svr_sup_feat_uuid = Uuid::From16Bit(GATT_UUID_SERVER_SUP_FEAT);
+  Uuid cl_sup_feat_uuid = Uuid::From16Bit(GATT_UUID_CLIENT_SUP_FEAT);
 
   btgatt_db_element_t service[] = {
       {
@@ -290,10 +358,22 @@
           .type = BTGATT_DB_PRIMARY_SERVICE,
       },
       {
-          .uuid = char_uuid,
+          .uuid = srv_changed_char_uuid,
           .type = BTGATT_DB_CHARACTERISTIC,
           .properties = GATT_CHAR_PROP_BIT_INDICATE,
           .permissions = 0,
+      },
+      {
+          .type = BTGATT_DB_CHARACTERISTIC,
+          .uuid = svr_sup_feat_uuid,
+          .properties = GATT_CHAR_PROP_BIT_READ,
+          .permissions = GATT_PERM_READ,
+      },
+      {
+          .type = BTGATT_DB_CHARACTERISTIC,
+          .uuid = cl_sup_feat_uuid,
+          .properties = GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE,
+          .permissions = GATT_PERM_READ | GATT_PERM_WRITE,
       }};
 
   GATTS_AddService(gatt_cb.gatt_if, service,
@@ -301,8 +381,13 @@
 
   service_handle = service[0].attribute_handle;
   gatt_cb.handle_of_h_r = service[1].attribute_handle;
+  gatt_cb.handle_sr_supported_feat = service[2].attribute_handle;
+  gatt_cb.handle_cl_supported_feat = service[3].attribute_handle;
 
-  VLOG(1) << __func__ << ": gatt_if=" << +gatt_cb.gatt_if;
+  gatt_cb.gatt_svr_supported_feat_mask |= BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK;
+  gatt_cb.gatt_cl_supported_feat_mask |= BLE_GATT_CL_SUP_FEAT_EATT_BITMASK;
+
+  VLOG(1) << __func__ << ": gatt_if=" << gatt_cb.gatt_if << " EATT supported";
 }
 
 /*******************************************************************************
@@ -366,6 +451,60 @@
   gatt_cl_start_config_ccc(p_clcb);
 }
 
+static void gatt_attr_send_is_eatt_cb(uint16_t conn_id, gatt_op_cb_data* cb,
+                                      bool eatt_supported) {
+  tGATT_IF gatt_if;
+  RawAddress bd_addr;
+  tBT_TRANSPORT transport;
+
+  GATT_GetConnectionInfor(conn_id, &gatt_if, bd_addr, &transport);
+
+  std::move(cb->cb).Run(bd_addr, eatt_supported);
+
+  cb->op_uuid = 0;
+}
+
+static bool gatt_svc_read_cl_supp_feat_req(uint16_t conn_id,
+                                           gatt_op_cb_data* cb) {
+  tGATT_READ_PARAM param;
+
+  memset(&param, 0, sizeof(tGATT_READ_PARAM));
+
+  param.service.s_handle = 1;
+  param.service.e_handle = 0xFFFF;
+  param.service.auth_req = 0;
+
+  param.service.uuid = bluetooth::Uuid::From16Bit(GATT_UUID_CLIENT_SUP_FEAT);
+
+  tGATT_STATUS status = GATTC_Read(conn_id, GATT_READ_BY_TYPE, &param);
+  if (status != GATT_SUCCESS) {
+    LOG(ERROR) << __func__ << " Read failed. Status: " << loghex(status);
+    return false;
+  }
+
+  cb->op_uuid = GATT_UUID_CLIENT_SUP_FEAT;
+  return true;
+}
+
+static bool gatt_att_write_cl_supp_feat(uint16_t conn_id, uint16_t handle) {
+  tGATT_VALUE attr;
+
+  memset(&attr, 0, sizeof(tGATT_VALUE));
+
+  attr.conn_id = conn_id;
+  attr.handle = handle;
+  attr.len = 1;
+  attr.value[0] = gatt_cb.gatt_cl_supported_feat_mask;
+
+  tGATT_STATUS status = GATTC_Write(conn_id, GATT_WRITE, &attr);
+  if (status != GATT_SUCCESS) {
+    LOG(ERROR) << __func__ << " Write failed. Status: " << loghex(status);
+    return false;
+  }
+
+  return true;
+}
+
 /*******************************************************************************
  *
  * Function         gatt_cl_op_cmpl_cback
@@ -375,10 +514,74 @@
  * Returns          void
  *
  ******************************************************************************/
-static void gatt_cl_op_cmpl_cback(UNUSED_ATTR uint16_t conn_id,
-                                  UNUSED_ATTR tGATTC_OPTYPE op,
-                                  UNUSED_ATTR tGATT_STATUS status,
-                                  UNUSED_ATTR tGATT_CL_COMPLETE* p_data) {}
+static void gatt_cl_op_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
+                                  tGATT_STATUS status,
+                                  tGATT_CL_COMPLETE* p_data) {
+  auto iter = OngoingOps.find(conn_id);
+
+  VLOG(1) << __func__ << " opcode: " << loghex(op)
+          << " status: " << loghex(status) << " conn id: " << loghex(conn_id);
+
+  if (op != GATTC_OPTYPE_READ) return;
+
+  if (iter == OngoingOps.end()) {
+    LOG(ERROR) << __func__ << " Unexpected read complete";
+    return;
+  }
+
+  gatt_op_cb_data* operation_callback_data = &iter->second;
+  uint16_t cl_op_uuid = operation_callback_data->op_uuid;
+
+  uint8_t* pp = p_data->att_value.value;
+
+  VLOG(1) << __func__ << " cl_op_uuid " << loghex(cl_op_uuid);
+
+  switch (cl_op_uuid) {
+    case GATT_UUID_SERVER_SUP_FEAT: {
+      uint8_t supported_feat_mask = 0;
+
+      /* Check if EATT is supported */
+      if (status == GATT_SUCCESS) {
+        STREAM_TO_UINT8(supported_feat_mask, pp);
+      }
+
+      /* Notify user if eatt is supported */
+      bool eatt_supported =
+          supported_feat_mask & BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK;
+      gatt_attr_send_is_eatt_cb(conn_id, operation_callback_data,
+                                eatt_supported);
+
+      /* If server supports EATT lets try to find handle for the
+       * client supported features characteristic, where we could write
+       * our supported features as a client.
+       */
+      if (eatt_supported) {
+        /* If read succeed, return here */
+        if (gatt_svc_read_cl_supp_feat_req(conn_id, operation_callback_data))
+          return;
+      }
+
+      /* Could not read client supported charcteristic or eatt is not
+       * supported. Erase callback data now.
+       */
+      OngoingOps.erase(iter);
+      break;
+    }
+    case GATT_UUID_CLIENT_SUP_FEAT:
+      /*We don't need callback data anymore */
+      OngoingOps.erase(iter);
+
+      if (status != GATT_SUCCESS) {
+        LOG(INFO) << __func__
+                  << " Client supported features charcteristic not found";
+        return;
+      }
+
+      /* Write our client supported features to the remote device */
+      gatt_att_write_cl_supp_feat(conn_id, p_data->att_value.handle);
+      break;
+  }
+}
 
 /*******************************************************************************
  *
@@ -457,3 +660,83 @@
   p_clcb->ccc_stage++;
   gatt_cl_start_config_ccc(p_clcb);
 }
+
+/*******************************************************************************
+ *
+ * Function         gatt_svc_read_supp_feat_req
+ *
+ * Description      Read remote device supported GATT feature mask.
+ *
+ * Returns          bool
+ *
+ ******************************************************************************/
+static bool gatt_svc_read_supp_feat_req(
+    const RawAddress& peer_bda, uint16_t conn_id,
+    base::OnceCallback<void(const RawAddress&, bool)> cb) {
+  tGATT_READ_PARAM param;
+  tGATT_PROFILE_CLCB* p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);
+
+  if (!p_clcb) {
+    p_clcb = gatt_profile_clcb_alloc(conn_id, peer_bda, BT_TRANSPORT_LE);
+  }
+
+  if (!p_clcb) {
+    VLOG(1) << __func__ << " p_clcb is NULL " << loghex(conn_id);
+    return false;
+  }
+
+  auto it = OngoingOps.find(conn_id);
+  if (it != OngoingOps.end()) {
+    LOG(ERROR) << __func__ << " There is ongoing operation for conn_id: "
+               << loghex(conn_id);
+    return false;
+  }
+
+  memset(&param, 0, sizeof(tGATT_READ_PARAM));
+
+  param.service.s_handle = 1;
+  param.service.e_handle = 0xFFFF;
+  param.service.auth_req = 0;
+
+  param.service.uuid = bluetooth::Uuid::From16Bit(GATT_UUID_SERVER_SUP_FEAT);
+
+  if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, &param) != GATT_SUCCESS) {
+    LOG(ERROR) << __func__ << " Read GATT Support features GATT_Read Failed";
+    return false;
+  }
+
+  gatt_op_cb_data cb_data;
+  cb_data.cb = std::move(cb);
+  cb_data.op_uuid = GATT_UUID_SERVER_SUP_FEAT;
+  OngoingOps[conn_id] = std::move(cb_data);
+
+  return true;
+}
+
+/*******************************************************************************
+ *
+ * Function         gatt_profile_get_eatt_support
+ *
+ * Description      Check if EATT is supported with remote device.
+ *
+ * Returns          false in case read could not be sent.
+ *
+ ******************************************************************************/
+bool gatt_profile_get_eatt_support(
+    const RawAddress& remote_bda,
+    base::OnceCallback<void(const RawAddress&, bool)> cb) {
+  uint16_t conn_id;
+
+  if (!cb) return false;
+
+  VLOG(1) << __func__ << " BDA: " << remote_bda
+          << " read gatt supported features";
+
+  GATT_GetConnIdIfConnected(gatt_cb.gatt_if, remote_bda, &conn_id,
+                            BT_TRANSPORT_LE);
+
+  /* This read is important only when connected */
+  if (conn_id == GATT_INVALID_CONN_ID) return false;
+
+  return gatt_svc_read_supp_feat_req(remote_bda, conn_id, std::move(cb));
+}
diff --git a/stack/gatt/gatt_int.h b/stack/gatt/gatt_int.h
index e072a47..9be52c6 100644
--- a/stack/gatt/gatt_int.h
+++ b/stack/gatt/gatt_int.h
@@ -26,6 +26,7 @@
 #include "gatt_api.h"
 #include "osi/include/fixed_queue.h"
 
+#include <base/bind.h>
 #include <base/strings/stringprintf.h>
 #include <string.h>
 #include <list>
@@ -379,6 +380,16 @@
   tGATT_PROFILE_CLCB profile_clcb[GATT_MAX_APPS];
   uint16_t
       handle_of_h_r; /* Handle of the handles reused characteristic value */
+  uint16_t handle_cl_supported_feat;
+  uint16_t handle_sr_supported_feat;
+  uint8_t
+      gatt_svr_supported_feat_mask; /* Local supported features as a server */
+
+  /* Supported features as a client. To be written to remote device.
+   * Note this is NOT a value of the characteristic with handle
+   * handle_cl_support_feat, as that one should be written by remote device.
+   */
+  uint8_t gatt_cl_supported_feat_mask;
 
   tGATT_APPL_INFO cb_info;
 
@@ -418,6 +429,10 @@
 /* from gatt_attr.cc */
 extern uint16_t gatt_profile_find_conn_id_by_bd_addr(const RawAddress& bda);
 
+extern bool gatt_profile_get_eatt_support(
+    const RawAddress& remote_bda,
+    base::OnceCallback<void(const RawAddress&, bool)> cb);
+
 /* Functions provided by att_protocol.cc */
 extern tGATT_STATUS attp_send_cl_msg(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
                                      uint8_t op_code, tGATT_CL_MSG* p_msg);
diff --git a/stack/gatt/gatt_main.cc b/stack/gatt/gatt_main.cc
index ed8d3fd..9935749 100644
--- a/stack/gatt/gatt_main.cc
+++ b/stack/gatt/gatt_main.cc
@@ -109,13 +109,6 @@
   gatt_cb.sign_op_queue = fixed_queue_new(SIZE_MAX);
   gatt_cb.srv_chg_clt_q = fixed_queue_new(SIZE_MAX);
   /* First, register fixed L2CAP channel for ATT over BLE */
-  fixed_reg.fixed_chnl_opts.mode = L2CAP_FCR_BASIC_MODE;
-  fixed_reg.fixed_chnl_opts.max_transmit = 0xFF;
-  fixed_reg.fixed_chnl_opts.rtrans_tout = 2000;
-  fixed_reg.fixed_chnl_opts.mon_tout = 12000;
-  fixed_reg.fixed_chnl_opts.mps = 670;
-  fixed_reg.fixed_chnl_opts.tx_win_sz = 1;
-
   fixed_reg.pL2CA_FixedConn_Cb = gatt_le_connect_cback;
   fixed_reg.pL2CA_FixedData_Cb = gatt_le_data_ind;
   fixed_reg.pL2CA_FixedCong_Cb = gatt_le_cong_cback; /* congestion callback */
@@ -125,7 +118,7 @@
 
   /* Now, register with L2CAP for ATT PSM over BR/EDR */
   if (!L2CA_Register(BT_PSM_ATT, (tL2CAP_APPL_INFO*)&dyn_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     LOG(ERROR) << "ATT Dynamic Registration failed";
   }
 
diff --git a/stack/hid/hidd_conn.cc b/stack/hid/hidd_conn.cc
index 55ec3b2..a239f73 100644
--- a/stack/hid/hidd_conn.cc
+++ b/stack/hid/hidd_conn.cc
@@ -763,13 +763,13 @@
   hd_cb.l2cap_intr_cfg.flush_to = HID_DEV_FLUSH_TO;
 
   if (!L2CA_Register(HID_PSM_CONTROL, (tL2CAP_APPL_INFO*)&dev_reg_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     HIDD_TRACE_ERROR("HID Control (device) registration failed");
     return (HID_ERR_L2CAP_FAILED);
   }
 
   if (!L2CA_Register(HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO*)&dev_reg_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     L2CA_Deregister(HID_PSM_CONTROL);
     HIDD_TRACE_ERROR("HID Interrupt (device) registration failed");
     return (HID_ERR_L2CAP_FAILED);
diff --git a/stack/hid/hidh_conn.cc b/stack/hid/hidh_conn.cc
index 3c694f0..944d859 100644
--- a/stack/hid/hidh_conn.cc
+++ b/stack/hid/hidh_conn.cc
@@ -98,12 +98,12 @@
 
   /* Now, register with L2CAP */
   if (!L2CA_Register(HID_PSM_CONTROL, (tL2CAP_APPL_INFO*)&hst_reg_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     HIDH_TRACE_ERROR("HID-Host Control Registration failed");
     return (HID_ERR_L2CAP_FAILED);
   }
   if (!L2CA_Register(HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO*)&hst_reg_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     L2CA_Deregister(HID_PSM_CONTROL);
     HIDH_TRACE_ERROR("HID-Host Interrupt Registration failed");
     return (HID_ERR_L2CAP_FAILED);
diff --git a/stack/include/a2dp_aac_encoder.h b/stack/include/a2dp_aac_encoder.h
index 143a577..5e909d8 100644
--- a/stack/include/a2dp_aac_encoder.h
+++ b/stack/include/a2dp_aac_encoder.h
@@ -21,8 +21,29 @@
 #ifndef A2DP_AAC_ENCODER_H
 #define A2DP_AAC_ENCODER_H
 
+#include "a2dp_aac_constants.h"
 #include "a2dp_codec_api.h"
 
+// Is used in btav_a2dp_codec_config_t.codec_specific_1 when codec is AAC
+enum class AacEncoderBitrateMode : int64_t {
+  // Variable bitrate mode unsupported when used in a codec report, and upper
+  // layer can use this value as system default (keep current settings)
+  AACENC_BR_MODE_CBR = A2DP_AAC_VARIABLE_BIT_RATE_DISABLED,
+  // Constant bitrate mode when Variable bitrate mode is supported. This can
+  // also be used to disable Variable bitrate mode by upper layer
+  AACENC_BR_MODE_VBR_C = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x00),
+  // Variable bitrate mode (very low bitrate for software encoding).
+  AACENC_BR_MODE_VBR_1 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x01),
+  // Variable bitrate mode (low bitrate for software encoding).
+  AACENC_BR_MODE_VBR_2 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x02),
+  // Variable bitrate mode (medium bitrate for software encoding).
+  AACENC_BR_MODE_VBR_3 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x03),
+  // Variable bitrate mode (high bitrate for software encoding).
+  AACENC_BR_MODE_VBR_4 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x04),
+  // Variable bitrate mode (very high bitrate for software encoding).
+  AACENC_BR_MODE_VBR_5 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x05),
+};
+
 // Loads the A2DP AAC encoder.
 // Return true on success, otherwise false.
 bool A2DP_LoadEncoderAac(void);
diff --git a/stack/include/a2dp_codec_api.h b/stack/include/a2dp_codec_api.h
index 45cbf9c..3297c5c 100644
--- a/stack/include/a2dp_codec_api.h
+++ b/stack/include/a2dp_codec_api.h
@@ -559,6 +559,15 @@
   // Decodes |p_buf| and calls |decode_callback| passed into init for the
   // decoded data.
   bool (*decode_packet)(BT_HDR* p_buf);
+
+  // Start the A2DP decoder.
+  void (*decoder_start)();
+
+  // Suspend the A2DP decoder.
+  void (*decoder_suspend)();
+
+  // A2DP decoder configuration.
+  void (*decoder_configure)(const uint8_t* p_codec_info);
 } tA2DP_DECODER_INTERFACE;
 
 // Gets the A2DP codec type.
diff --git a/stack/include/a2dp_vendor_ldac_decoder.h b/stack/include/a2dp_vendor_ldac_decoder.h
index 2a05baa..5efede8 100644
--- a/stack/include/a2dp_vendor_ldac_decoder.h
+++ b/stack/include/a2dp_vendor_ldac_decoder.h
@@ -40,4 +40,13 @@
 // |a2dp_vendor_ldac_decoder_init| if decoded frames are available.
 bool a2dp_vendor_ldac_decoder_decode_packet(BT_HDR* p_buf);
 
+// Start the A2DP LDAC decoder.
+void a2dp_vendor_ldac_decoder_start(void);
+
+// Suspend the A2DP LDAC decoder.
+void a2dp_vendor_ldac_decoder_suspend(void);
+
+// A2DP LDAC decoder configuration.
+void a2dp_vendor_ldac_decoder_configure(const uint8_t* p_codec_info);
+
 #endif  // A2DP_VENDOR_LDAC_DECODER_H
diff --git a/stack/include/avdt_api.h b/stack/include/avdt_api.h
index 09e7f7c..46b2e5f 100644
--- a/stack/include/avdt_api.h
+++ b/stack/include/avdt_api.h
@@ -931,19 +931,6 @@
 
 /*******************************************************************************
  *
- * Function         AVDT_GetSignalChannel
- *
- * Description      Get the L2CAP CID used by the signal channel of the given
- *                  handle.
- *
- * Returns          CID if successful, otherwise 0.
- *
- ******************************************************************************/
-extern uint16_t AVDT_GetSignalChannel(uint8_t handle,
-                                      const RawAddress& bd_addr);
-
-/*******************************************************************************
- *
  * Function         AVDT_SendReport
  *
  * Description
diff --git a/stack/include/avrc_defs.h b/stack/include/avrc_defs.h
index d1c844f..29d3e39 100644
--- a/stack/include/avrc_defs.h
+++ b/stack/include/avrc_defs.h
@@ -358,7 +358,8 @@
 #define AVRC_MEDIA_ATTR_ID_NUM_TRACKS 0x00000005
 #define AVRC_MEDIA_ATTR_ID_GENRE 0x00000006
 #define AVRC_MEDIA_ATTR_ID_PLAYING_TIME 0x00000007 /* in miliseconds */
-#define AVRC_MAX_NUM_MEDIA_ATTR_ID 7
+#define AVRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE 0x00000008
+#define AVRC_MAX_NUM_MEDIA_ATTR_ID 8
 
 /* Define the possible values of play state
 */
@@ -966,7 +967,7 @@
 
 #define AVRC_IS_VALID_MEDIA_ATTRIBUTE(a)            \
   (((a) >= AVRC_MEDIA_ATTR_ID_TITLE) &&             \
-           ((a) <= AVRC_MEDIA_ATTR_ID_PLAYING_TIME) \
+           ((a) <= AVRC_MAX_NUM_MEDIA_ATTR_ID) \
        ? true                                       \
        : false)
 
diff --git a/stack/include/bt_types.h b/stack/include/bt_types.h
index 01e8248..aec30d3 100644
--- a/stack/include/bt_types.h
+++ b/stack/include/bt_types.h
@@ -641,9 +641,9 @@
 } FLOW_SPEC;
 
 /* Values for service_type */
-#define NO_TRAFFIC 0
-#define BEST_EFFORT 1
-#define GUARANTEED 2
+#define SVC_TYPE_NO_TRAFFIC 0
+#define SVC_TYPE_BEST_EFFORT 1
+#define SVC_TYPE_GUARANTEED 2
 
 /* Service class of the CoD */
 #define SERV_CLASS_NETWORKING (1 << 1)
diff --git a/stack/include/btm_api.h b/stack/include/btm_api.h
index df4cbe0..7c6ff89 100644
--- a/stack/include/btm_api.h
+++ b/stack/include/btm_api.h
@@ -309,45 +309,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetPeriodicInquiryMode
- *
- * Description      This function is called to set the device periodic inquiry
- *                  mode. If the duration is zero, the periodic inquiry mode is
- *                  cancelled.
- *
- * Parameters:      p_inqparms - pointer to the inquiry information
- *                      mode - GENERAL or LIMITED inquiry
- *                      duration - length in 1.28 sec intervals (If '0', the
- *                                 inquiry is CANCELLED)
- *                      max_resps - maximum amount of devices to search for
- *                                  before ending the inquiry
- *                      filter_cond_type - BTM_CLR_INQUIRY_FILTER,
- *                                         BTM_FILTER_COND_DEVICE_CLASS, or
- *                                         BTM_FILTER_COND_BD_ADDR
- *                      filter_cond - value for the filter (based on
- *                                                          filter_cond_type)
- *
- *                  max_delay - maximum amount of time between successive
- *                              inquiries
- *                  min_delay - minimum amount of time between successive
- *                              inquiries
- *                  p_results_cb - callback returning pointer to results
- *                              (tBTM_INQ_RESULTS)
- *
- * Returns          BTM_CMD_STARTED if successfully started
- *                  BTM_ILLEGAL_VALUE if a bad parameter is detected
- *                  BTM_NO_RESOURCES if could not allocate a message buffer
- *                  BTM_SUCCESS - if cancelling the periodic inquiry
- *                  BTM_BUSY - if an inquiry is already active
- *                  BTM_WRONG_MODE if the device is not up.
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SetPeriodicInquiryMode(
-    tBTM_INQ_PARMS* p_inqparms, uint16_t max_delay, uint16_t min_delay,
-    tBTM_INQ_RESULTS_CB* p_results_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_StartInquiry
  *
  * Description      This function is called to start an inquiry.
@@ -588,53 +549,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadRemoteExtendedFeatures
- *
- * Description      This function is called to read a specific extended features
- *                  page of the remote device
- *
- *                  Note1: The size of device features mask page is
- *                  BTM_FEATURE_BYTES_PER_PAGE bytes.
- *                  Note2: The valid device features mask page number depends on
- *                  the remote device capabilities. It is expected to be in the
- *                  range [0 - BTM_EXT_FEATURES_PAGE_MAX].
-
- * Returns          pointer to the remote extended features mask
- *                  or NULL if page_number is not valid
- *
- ******************************************************************************/
-extern uint8_t* BTM_ReadRemoteExtendedFeatures(const RawAddress& addr,
-                                               uint8_t page_number);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadNumberRemoteFeaturesPages
- *
- * Description      This function is called to retrieve the number of feature
- *                  pages read from the remote device
- *
- * Returns          number of features pages read from the remote device
- *
- ******************************************************************************/
-extern uint8_t BTM_ReadNumberRemoteFeaturesPages(const RawAddress& addr);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadAllRemoteFeatures
- *
- * Description      Read all the features of the remote device
- *
- * Returns          pointer to the byte[0] of the page[0] of the remote device
- *                  feature mask.
- *
- * Note:            the function returns the pointer to the array of the size
- *                  BTM_FEATURE_BYTES_PER_PAGE * (BTM_EXT_FEATURES_PAGE_MAX + 1)
- *
- ******************************************************************************/
-extern uint8_t* BTM_ReadAllRemoteFeatures(const RawAddress& addr);
-
-/*******************************************************************************
- *
  * Function         BTM_InqDbRead
  *
  * Description      This function looks through the inquiry database for a match
@@ -690,20 +604,6 @@
  ******************************************************************************/
 extern tBTM_STATUS BTM_ClearInqDb(const RawAddress* p_bda);
 
-/*******************************************************************************
- *
- * Function         BTM_ReadInquiryRspTxPower
- *
- * Description      This command will read the inquiry Transmit Power level used
- *                  to transmit the FHS and EIR data packets.
- *                  This can be used directly in the Tx Power Level EIR data
- *                  type.
- *
- * Returns          BTM_SUCCESS if successful
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_ReadInquiryRspTxPower(tBTM_CMPL_CB* p_cb);
-
 /*****************************************************************************
  *  ACL CHANNEL MANAGEMENT FUNCTIONS
  ****************************************************************************/
@@ -889,24 +789,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadLinkQuality
- *
- * Description      This function is called to read the link quality.
- *                  The value of the link quality is returned in the callback.
- *                  (tBTM_LINK_QUALITY_RESULT)
- *
- * Returns          BTM_CMD_STARTED if command issued to controller.
- *                  BTM_NO_RESOURCES if memory couldn't be allocated to issue
- *                                   the command
- *                  BTM_UNKNOWN_ADDR if no active link with bd addr specified
- *                  BTM_BUSY if command is already in progress
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_ReadLinkQuality(const RawAddress& remote_bda,
-                                       tBTM_CMPL_CB* p_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_RegBusyLevelNotif
  *
  * Description      This function is called to register a callback to receive
@@ -921,18 +803,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_AclRegisterForChanges
- *
- * Description      This function is called to register a callback to receive
- *                  ACL database change events, i.e. new connection or removed.
- *
- * Returns          BTM_SUCCESS if successfully initiated, otherwise error
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_AclRegisterForChanges(tBTM_ACL_DB_CHANGE_CB* p_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_GetNumAclLinks
  *
  * Description      This function is called to count the number of
@@ -943,18 +813,6 @@
  ******************************************************************************/
 extern uint16_t BTM_GetNumAclLinks(void);
 
-/*******************************************************************************
- *
- * Function         BTM_SetQoS
- *
- * Description      This function is called to setup QoS
- *
- * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SetQoS(const RawAddress& bd, FLOW_SPEC* p_flow,
-                              tBTM_CMPL_CB* p_cb);
-
 /*****************************************************************************
  *  (e)SCO CHANNEL MANAGEMENT FUNCTIONS
  ****************************************************************************/
@@ -993,71 +851,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetScoPacketTypes
- *
- * Description      This function is called to set the packet types used for
- *                  a specific SCO connection,
- *
- * Parameters       pkt_types - One or more of the following
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *
- *                  BTM_SCO_LINK_ALL_MASK   - enables all supported types
- *
- * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SetScoPacketTypes(uint16_t sco_inx, uint16_t pkt_types);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoPacketTypes
- *
- * Description      This function is read the packet types used for a specific
- *                  SCO connection.
- *
- * Returns       One or more of the following (bitmask)
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *
- * Returns          packet types supported for the connection
- *
- ******************************************************************************/
-extern uint16_t BTM_ReadScoPacketTypes(uint16_t sco_inx);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadDeviceScoPacketTypes
- *
- * Description      This function is read the SCO packet types that
- *                  the device supports.
- *
- * Returns          packet types supported by the device.
- *
- ******************************************************************************/
-extern uint16_t BTM_ReadDeviceScoPacketTypes(void);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoHandle
- *
- * Description      Reead the HCI handle used for a specific SCO connection,
- *
- * Returns          handle for the connection, or 0xFFFF if invalid SCO index.
- *
- ******************************************************************************/
-extern uint16_t BTM_ReadScoHandle(uint16_t sco_inx);
-
-/*******************************************************************************
- *
  * Function         BTM_ReadScoBdAddr
  *
  * Description      This function is read the remote BD Address for a specific
@@ -1070,19 +863,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadScoDiscReason
- *
- * Description      This function is returns the reason why an (e)SCO connection
- *                  has been removed. It contains the value until read, or until
- *                  another (e)SCO connection has disconnected.
- *
- * Returns          HCI reason or BTM_INVALID_SCO_DISC_REASON if not set.
- *
- ******************************************************************************/
-extern uint16_t BTM_ReadScoDiscReason(void);
-
-/*******************************************************************************
- *
  * Function         BTM_SetEScoMode
  *
  * Description      This function sets up the negotiated parameters for SCO or
@@ -1098,19 +878,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetWBSCodec
- *
- * Description      This function sends command to the controller to setup
- *                  WBS codec for the upcoming eSCO connection.
- *
- * Returns          BTM_SUCCESS.
- *
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SetWBSCodec(tBTM_SCO_CODEC_TYPE codec_type);
-
-/*******************************************************************************
- *
  * Function         BTM_RegForEScoEvts
  *
  * Description      This function registers a SCO event callback with the
@@ -1127,30 +894,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadEScoLinkParms
- *
- * Description      This function returns the current eSCO link parameters for
- *                  the specified handle.  This can be called anytime a
- *                  connection is active, but is typically called after
- *                  receiving the SCO opened callback.
- *
- *                  Note: If called over a 1.1 controller, only the packet types
- *                        field has meaning.
- *                  Note: If the upper layer doesn't know the current sco index,
- *                  BTM_FIRST_ACTIVE_SCO_INDEX can be used as the first
- *                  parameter to find the first active SCO index
- *
- * Returns          BTM_SUCCESS if returned data is valid connection.
- *                  BTM_ILLEGAL_VALUE if no connection for specified sco_inx.
- *                  BTM_MODE_UNSUPPORTED if local controller does not support
- *                      1.2 specification.
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_ReadEScoLinkParms(uint16_t sco_inx,
-                                         tBTM_ESCO_DATA* p_parms);
-
-/*******************************************************************************
- *
  * Function         BTM_ChangeEScoLinkParms
  *
  * Description      This function requests renegotiation of the parameters on
@@ -1222,19 +965,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecRegisterLinkKeyNotificationCallback
- *
- * Description      Profiles can register to be notified when a new Link Key
- *                  is generated per connection.
- *
- * Returns          true if registered OK, else false
- *
- ******************************************************************************/
-extern bool BTM_SecRegisterLinkKeyNotificationCallback(
-    tBTM_LINK_KEY_CALLBACK* p_callback);
-
-/*******************************************************************************
- *
  * Function         BTM_SecAddRmtNameNotifyCallback
  *
  * Description      Profiles can register to be notified when name of the
@@ -1332,24 +1062,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetSecureConnectionsOnly
- *
- * Description      Enable or disable default treatment for Mode 4 Level 0
- *                  services
- *
- * Parameter        secure_connections_only_mode - (true or false)
- *                  true means that the device should treat Mode 4 Level 0
- *                  services as services of other levels.
- *                  false means that the device should provide default
- *                  treatment for Mode 4 Level 0 services.
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTM_SetSecureConnectionsOnly(bool secure_connections_only_mode);
-
-/*******************************************************************************
- *
  * Function         BTM_SetSecurityLevel
  *
  * Description      Register service security level with Security Manager.  Each
@@ -1435,21 +1147,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecGetDeviceLinkKey
- *
- * Description      This function is called to obtain link key for the device
- *                  it returns BTM_SUCCESS if link key is available, or
- *                  BTM_UNKNOWN_ADDR if Security Manager does not know about
- *                  the device or device record does not contain link key info
- *
- * Returns          BTM_SUCCESS if successful, otherwise error code
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SecGetDeviceLinkKey(const RawAddress& bd_addr,
-                                           LinkKey* link_key);
-
-/*******************************************************************************
- *
  * Function         BTM_SecGetDeviceLinkKeyType
  *
  * Description      This function is called to obtain link key type for the
@@ -1492,26 +1189,10 @@
  *
  * Function         BTM_SecBond
  *
- * Description      This function is called to perform bonding with peer device.
- *
- * Parameters:      bd_addr      - Address of the device to bond
- *                  pin_len      - length in bytes of the PIN Code
- *                  p_pin        - pointer to array with the PIN Code
- *                  trusted_mask - bitwise OR of trusted services
- *                                 (array of uint32_t)
- * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, uint8_t pin_len,
-                               uint8_t* p_pin, uint32_t trusted_mask[]);
-
-/*******************************************************************************
- *
- * Function         BTM_SecBondByTransport
- *
  * Description      Perform bonding by designated transport
  *
  * Parameters:      bd_addr      - Address of the device to bond
+ *                  addr_type    - address type for LE transport
  *                  pin_len      - length in bytes of the PIN Code
  *                  p_pin        - pointer to array with the PIN Code
  *                  trusted_mask - bitwise OR of trusted services
@@ -1522,10 +1203,11 @@
  * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
  *
  ******************************************************************************/
-extern tBTM_STATUS BTM_SecBondByTransport(const RawAddress& bd_addr,
-                                          tBT_TRANSPORT transport,
-                                          uint8_t pin_len, uint8_t* p_pin,
-                                          uint32_t trusted_mask[]);
+extern tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr,
+                               tBLE_ADDR_TYPE addr_type,
+                               tBT_TRANSPORT transport, int device_type,
+                               uint8_t pin_len, uint8_t* p_pin,
+                               uint32_t trusted_mask[]);
 
 /*******************************************************************************
  *
@@ -1666,28 +1348,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_BuildOobData
- *
- * Description      This function is called to build the OOB data payload to
- *                  be sent over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  max_len - p_data size.
- *                  c       - simple pairing Hash C.
- *                  r       - simple pairing Randomizer  C.
- *                  name_len- 0, local device name would not be included.
- *                            otherwise, the local device name is included for
- *                            up to this specified length
- *
- * Returns          Number of bytes in p_data.
- *
- ******************************************************************************/
-extern uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len,
-                                 const Octet16& c, const Octet16& r,
-                                 uint8_t name_len);
-
-/*******************************************************************************
- *
  * Function         BTM_BothEndsSupportSecureConnections
  *
  * Description      This function is called to check if both the local device
@@ -1720,24 +1380,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadOobData
- *
- * Description      This function is called to parse the OOB data payload
- *                  received over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  eir_tag - The associated EIR tag to read the data.
- *                  *p_len(output) - the length of the data with the given tag.
- *
- * Returns          the beginning of the data with the given tag.
- *                  NULL, if the tag is not found.
- *
- ******************************************************************************/
-extern uint8_t* BTM_ReadOobData(uint8_t* p_data, uint8_t eir_tag,
-                                uint8_t* p_len);
-
-/*******************************************************************************
- *
  * Function         BTM_SecReadDevName
  *
  * Description      Looks for the device name in the security database for the
@@ -1977,85 +1619,6 @@
                                   uint8_t uuid_size, uint8_t* p_num_uuid,
                                   uint8_t* p_uuid_list, uint8_t max_num_uuid);
 
-/*****************************************************************************
- *  SCO OVER HCI
- ****************************************************************************/
-/*******************************************************************************
- *
- * Function         BTM_ConfigScoPath
- *
- * Description      This function enable/disable SCO over HCI and registers SCO
- *                  data callback if SCO over HCI is enabled.
- *
- * Parameter        path: SCO or HCI
- *                  p_sco_data_cb: callback function or SCO data if path is set
- *                                 to transport.
- *                  p_pcm_param: pointer to the PCM interface parameter. If a
- *                               NULL pointer is used, the PCM parameter
- *                               maintained in the control block will be used;
- *                               otherwise update the control block value.
- *                  err_data_rpt: Lisbon feature to enable the erronous data
- *                                report or not.
- *
- * Returns          BTM_SUCCESS if the successful.
- *                  BTM_NO_RESOURCES: no rsource to start the command.
- *                  BTM_ILLEGAL_VALUE: invalid callback function pointer.
- *                  BTM_CMD_STARTED : Command sent. Waiting for command
- *                                    complete event.
- *
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_ConfigScoPath(esco_data_path_t path,
-                                     tBTM_SCO_DATA_CB* p_sco_data_cb,
-                                     tBTM_SCO_PCM_PARAM* p_pcm_param,
-                                     bool err_data_rpt);
-
-/*******************************************************************************
- *
- * Function         BTM_WriteScoData
- *
- * Description      This function write SCO data to a specified instance. The
- *                  data to be written p_buf needs to carry an offset of
- *                  HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not
- *                  exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is
- *                  set to 60 and is configurable. Data longer than the maximum
- *                  bytes will be truncated.
- *
- * Returns          BTM_SUCCESS: data write is successful
- *                  BTM_ILLEGAL_VALUE: SCO data contains illegal offset value.
- *                  BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO
- *                                      packet size.
- *                  BTM_NO_RESOURCES: no resources.
- *                  BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is
- *                                    not routed via HCI.
- *
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_WriteScoData(uint16_t sco_inx, BT_HDR* p_buf);
-
-/*******************************************************************************
- *
- * Function         BTM_SetARCMode
- *
- * Description      Send Audio Routing Control command.
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTM_SetARCMode(uint8_t iface, uint8_t arc_mode,
-                           tBTM_VSC_CMPL_CB* p_arc_cb);
-
-/*******************************************************************************
- *
- * Function         BTM_PCM2Setup_Write
- *
- * Description      Send PCM2_Setup write command.
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTM_PCM2Setup_Write(bool clk_master, tBTM_VSC_CMPL_CB* p_arc_cb);
-
 /*******************************************************************************
  *
  * Function         BTM_PM_ReadControllerState
diff --git a/stack/include/btm_api_types.h b/stack/include/btm_api_types.h
index d14b754..b291ce5 100755
--- a/stack/include/btm_api_types.h
+++ b/stack/include/btm_api_types.h
@@ -573,9 +573,8 @@
 /***************************
  *  Device Discovery Types
  ***************************/
-/* Definitions of the parameters passed to BTM_StartInquiry and
- * BTM_SetPeriodicInquiryMode.
-*/
+/* Definitions of the parameters passed to BTM_StartInquiry.
+ */
 typedef struct /* contains the two device class condition fields */
 {
   DEV_CLASS dev_class;
@@ -1046,9 +1045,6 @@
 #define BTM_SEC_MODE_SP_DEBUG 5
 #define BTM_SEC_MODE_SC 6
 
-/* Maximum Number of BTM Security Modes */
-#define BTM_SEC_MODES_MAX 7
-
 /* Security Service Levels [bit mask] (BTM_SetSecurityLevel)
  * Encryption should not be used without authentication
 */
@@ -1118,15 +1114,10 @@
 typedef uint8_t tBTM_LINK_KEY_TYPE;
 
 /* Protocol level security (BTM_SetSecurityLevel) */
-#define BTM_SEC_PROTO_L2CAP 0
-#define BTM_SEC_PROTO_SDP 1
-#define BTM_SEC_PROTO_TCS 2
 #define BTM_SEC_PROTO_RFCOMM 3
-#define BTM_SEC_PROTO_OBEX 4
 #define BTM_SEC_PROTO_BNEP 5
 #define BTM_SEC_PROTO_HID 6 /* HID      */
 #define BTM_SEC_PROTO_AVDT 7
-#define BTM_SEC_PROTO_MCA 8
 
 /* Determine the number of uint32_t's necessary for security services */
 #define BTM_SEC_ARRAY_BITS 32 /* Number of bits in each array element */
@@ -1240,62 +1231,6 @@
       ((uint32_t*)(p_dst))[trst] = 0;                         \
   }
 
-/* Following bits can be provided by host in the trusted_mask array */
-/* 0..31 bits of mask[0] (Least Significant Word) */
-#define BTM_SEC_TRUST_SDP_SERVER (1 << BTM_SEC_SERVICE_SDP_SERVER)
-#define BTM_SEC_TRUST_SERIAL_PORT (1 << BTM_SEC_SERVICE_SERIAL_PORT)
-#define BTM_SEC_TRUST_LAN_ACCESS (1 << BTM_SEC_SERVICE_LAN_ACCESS)
-#define BTM_SEC_TRUST_DUN (1 << BTM_SEC_SERVICE_DUN)
-#define BTM_SEC_TRUST_IRMC_SYNC (1 << BTM_SEC_SERVICE_IRMC_SYNC)
-#define BTM_SEC_TRUST_IRMC_SYNC_CMD (1 << BTM_SEC_SERVICE_IRMC_SYNC_CMD)
-#define BTM_SEC_TRUST_OBEX (1 << BTM_SEC_SERVICE_OBEX)
-#define BTM_SEC_TRUST_OBEX_FTP (1 << BTM_SEC_SERVICE_OBEX_FTP)
-#define BTM_SEC_TRUST_HEADSET (1 << BTM_SEC_SERVICE_HEADSET)
-#define BTM_SEC_TRUST_CORDLESS (1 << BTM_SEC_SERVICE_CORDLESS)
-#define BTM_SEC_TRUST_INTERCOM (1 << BTM_SEC_SERVICE_INTERCOM)
-#define BTM_SEC_TRUST_FAX (1 << BTM_SEC_SERVICE_FAX)
-#define BTM_SEC_TRUST_HEADSET_AG (1 << BTM_SEC_SERVICE_HEADSET_AG)
-#define BTM_SEC_TRUST_PNP_INFO (1 << BTM_SEC_SERVICE_PNP_INFO)
-#define BTM_SEC_TRUST_GEN_NET (1 << BTM_SEC_SERVICE_GEN_NET)
-#define BTM_SEC_TRUST_GEN_FILE (1 << BTM_SEC_SERVICE_GEN_FILE)
-#define BTM_SEC_TRUST_GEN_AUDIO (1 << BTM_SEC_SERVICE_GEN_AUDIO)
-#define BTM_SEC_TRUST_GEN_TEL (1 << BTM_SEC_SERVICE_GEN_TEL)
-#define BTM_SEC_TRUST_CTP_DATA (1 << BTM_SEC_SERVICE_CTP_DATA)
-#define BTM_SEC_TRUST_HCRP_CTRL (1 << BTM_SEC_SERVICE_HCRP_CTRL)
-#define BTM_SEC_TRUST_HCRP_DATA (1 << BTM_SEC_SERVICE_HCRP_DATA)
-#define BTM_SEC_TRUST_HCRP_NOTIF (1 << BTM_SEC_SERVICE_HCRP_NOTIF)
-#define BTM_SEC_TRUST_BPP_JOB (1 << BTM_SEC_SERVICE_JOB)
-#define BTM_SEC_TRUST_BPP_STATUS (1 << BTM_SEC_SERVICE_STATUS)
-#define BTM_SEC_TRUST_BPP_REF (1 << BTM_SEC_SERVICE_REF)
-#define BTM_SEC_TRUST_BNEP_PANU (1 << BTM_SEC_SERVICE_BNEP_PANU)
-#define BTM_SEC_TRUST_BNEP_GN (1 << BTM_SEC_SERVICE_BNEP_GN)
-#define BTM_SEC_TRUST_BNEP_NAP (1 << BTM_SEC_SERVICE_BNEP_NAP)
-#define BTM_SEC_TRUST_HFP_HF (1 << BTM_SEC_SERVICE_HF_HANDSFREE)
-#define BTM_SEC_TRUST_HFP_AG (1 << BTM_SEC_SERVICE_AG_HANDSFREE)
-#define BTM_SEC_TRUST_TE_PHONE_ACCESS (1 << BTM_SEC_SERVICE_TE_PHONE_ACCESS)
-#define BTM_SEC_TRUST_ME_PHONE_ACCESS (1 << BTM_SEC_SERVICE_ME_PHONE_ACCESS)
-
-/* 0..31 bits of mask[1] (Most Significant Word) */
-#define BTM_SEC_TRUST_HIDH_CTRL (1 << (BTM_SEC_SERVICE_HIDH_SEC_CTRL - 32))
-#define BTM_SEC_TRUST_HIDH_NOSEC_CTRL \
-  (1 << (BTM_SEC_SERVICE_HIDH_NOSEC_CTRL - 32))
-#define BTM_SEC_TRUST_HIDH_INTR (1 << (BTM_SEC_SERVICE_HIDH_INTR - 32))
-#define BTM_SEC_TRUST_BIP (1 << (BTM_SEC_SERVICE_BIP - 32))
-#define BTM_SEC_TRUST_BIP_REF (1 << (BTM_SEC_SERVICE_BIP_REF - 32))
-#define BTM_SEC_TRUST_AVDTP (1 << (BTM_SEC_SERVICE_AVDTP - 32))
-#define BTM_SEC_TRUST_AVDTP_NOSEC (1 << (BTM_SEC_SERVICE_AVDTP_NOSEC - 32))
-#define BTM_SEC_TRUST_AVCTP (1 << (BTM_SEC_SERVICE_AVCTP - 32))
-#define BTM_SEC_TRUST_SAP (1 << (BTM_SEC_SERVICE_SAP - 32))
-#define BTM_SEC_TRUST_PBAP (1 << (BTM_SEC_SERVICE_PBAP - 32))
-#define BTM_SEC_TRUST_RFC_MUX (1 << (BTM_SEC_SERVICE_RFC_MUX - 32))
-#define BTM_SEC_TRUST_AVCTP_BROWSE (1 << (BTM_SEC_SERVICE_AVCTP_BROWSE - 32))
-#define BTM_SEC_TRUST_MAP (1 << (BTM_SEC_SERVICE_MAP - 32))
-#define BTM_SEC_TRUST_MAP_NOTIF (1 << (BTM_SEC_SERVICE_MAP_NOTIF - 32))
-#define BTM_SEC_TRUST_MCAP_CTRL (1 << (BTM_SEC_SERVICE_MCAP_CTRL - 32))
-#define BTM_SEC_TRUST_MCAP_DATA (1 << (BTM_SEC_SERVICE_MCAP_DATA - 32))
-#define BTM_SEC_TRUST_HDP_SNK (1 << (BTM_SEC_SERVICE_HDP_SNK - 32))
-#define BTM_SEC_TRUST_HDP_SRC (1 << (BTM_SEC_SERVICE_HDP_SRC - 32))
-
 #define BTM_SEC_TRUST_ALL 0xFFFFFFFF /* for each array element */
 
 /****************************************
diff --git a/stack/include/btm_ble_api_types.h b/stack/include/btm_ble_api_types.h
index 9f0201c..6c59a75 100644
--- a/stack/include/btm_ble_api_types.h
+++ b/stack/include/btm_ble_api_types.h
@@ -36,10 +36,12 @@
 #define BTM_BLE_NON_CONNECT_EVT 0x03
 /* Connectable low duty cycle directed advertising  */
 #define BTM_BLE_CONNECT_LO_DUTY_DIR_EVT 0x04
-/* 0x00 - 0x05 can be received on adv event type */
+/* 0x00 - 0x04 can be received on adv event type */
+#define BTM_BLE_ADV_IND_EVT  0x00
+#define BTM_BLE_ADV_DIRECT_IND_EVT  0x01
+#define BTM_BLE_ADV_SCAN_IND_EVT  0x02
+#define BTM_BLE_ADV_NONCONN_IND_EVT  0x03
 #define BTM_BLE_SCAN_RSP_EVT 0x04
-#define BTM_BLE_SCAN_REQ_EVT 0x05
-#define BTM_BLE_UNKNOWN_EVT 0xff
 
 #define BTM_BLE_UNKNOWN_EVT 0xff
 
diff --git a/stack/include/btu.h b/stack/include/btu.h
index 77d5051..03fe2a1 100644
--- a/stack/include/btu.h
+++ b/stack/include/btu.h
@@ -62,6 +62,9 @@
 base::MessageLoop* get_main_message_loop();
 bt_status_t do_in_main_thread(const base::Location& from_here,
                               base::OnceClosure task);
+bt_status_t do_in_main_thread_delayed(const base::Location& from_here,
+                                      base::OnceClosure task,
+                                      const base::TimeDelta& delay);
 
 void BTU_StartUp(void);
 void BTU_ShutDown(void);
diff --git a/stack/include/gap_api.h b/stack/include/gap_api.h
index 9293378..5fcd8cc 100644
--- a/stack/include/gap_api.h
+++ b/stack/include/gap_api.h
@@ -83,7 +83,6 @@
 /* GAP_ConnOpen() - optional channels to negotiate */
 #define GAP_FCR_CHAN_OPT_BASIC L2CAP_FCR_CHAN_OPT_BASIC
 #define GAP_FCR_CHAN_OPT_ERTM L2CAP_FCR_CHAN_OPT_ERTM
-#define GAP_FCR_CHAN_OPT_STREAM L2CAP_FCR_CHAN_OPT_STREAM
 /*** used in connection variables and functions ***/
 #define GAP_INVALID_HANDLE 0xFFFF
 
diff --git a/stack/include/gattdefs.h b/stack/include/gattdefs.h
index 186542c..8b3c91c 100644
--- a/stack/include/gattdefs.h
+++ b/stack/include/gattdefs.h
@@ -131,4 +131,8 @@
 #define GATT_UUID_SCAN_INT_WINDOW 0x2A4F
 #define GATT_UUID_SCAN_REFRESH 0x2A31
 
+/* GATT Service characteristics */
+#define GATT_UUID_SERVER_SUP_FEAT 0x2B3A
+#define GATT_UUID_CLIENT_SUP_FEAT 0x2B29
+
 #endif
diff --git a/stack/include/hci_evt_length.h b/stack/include/hci_evt_length.h
new file mode 100644
index 0000000..ea8dfdf
--- /dev/null
+++ b/stack/include/hci_evt_length.h
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+/*
+ *  Definitions for HCI Event Parameter Minimum Length
+ */
+static const uint8_t hci_event_parameters_minimum_length[] = {
+    0,    //  0x00 - N/A
+    1,    //  0x01 - HCI_Inquiry_Complete Event
+    15,   //  0x02 - HCI_Inquiry_Result Event (Num_Responses = 1)
+    11,   //  0x03 - HCI_Connection_Complete Event
+    10,   //  0x04 - HCI_Connection_Request Event
+    4,    //  0x05 - HCI_Disconnection_Complete Event
+    3,    //  0x06 - HCI_Authentication_Complete Event
+    255,  //  0x07 - HCI_Remote_Name_Request_Complete Event
+    4,    //  0x08 - HCI_Encryption_Change Event
+    3,    //  0x09 - HCI_Change_Connection_Link_Key_Complete Event
+    4,    //  0x0A - HCI_Master_Link_Key_Complete Event
+    11,   //  0x0B - HCI_Read_Remote_Supported_Features_Complete Event
+    8,    //  0x0C - HCI_Read_Remote_Version_Information_Complete Event
+    21,   //  0x0D - HCI_QoS_Setup_Complete Event
+    3,    //  0x0E - HCI_Command_Complete Event (Depends on command)
+    4,    //  0x0F - HCI_Command_Status Event
+    1,    //  0x10 - HCI_Hardware_Error Event
+    2,    //  0x11 - HCI_Flush_Occurred Event
+    8,    //  0x12 - HCI_Role_Change Event
+    5,    //  0x13 - HCI_Number_Of_Completed_Packets Event (Num_Handles = 1)
+    6,    //  0x14 - HCI_Mode_Change Event
+    23,   //  0x15 - HCI_Return_Link_Keys Event (Num_Keys = 1)
+    6,    //  0x16 - HCI_PIN_Code_Request Event
+    6,    //  0x17 - HCI_Link_Key_Request Event
+    23,   //  0x18 - HCI_Link_Key_Notification Event
+    3,    //  0x19 - HCI_Loopback_Command Event (Depends on command)
+    1,    //  0x1A - HCI_Data_Buffer_Overflow Event
+    3,    //  0x1B - HCI_Max_Slots_Change Event
+    5,    //  0x1C - HCI_Read_Clock_Offset_Complete Event
+    5,    //  0x1D - HCI_Connection_Packet_Type_Changed Event
+    2,    //  0x1E - HCI_QoS_Violation Event
+    7,    //  0x1F - HCI_Page_Scan_Mode_Change Event (Deprecated)
+    7,    //  0x20 - HCI_Page_Scan_Repetition_Mode_Change Event
+    22,   //  0x21 - HCI_Flow_Specification_Complet Event
+    15,   //  0x22 - HCI_Inquiry_Result_with_RSSI Event (Num_Responses = 1)
+    13,   //  0x23 - HCI_Read_Remote_Extended_Features_Complete Event
+    0,    //  0x24 - N/A
+    0,    //  0x25 - N/A
+    0,    //  0x26 - N/A
+    0,    //  0x27 - N/A
+    0,    //  0x28 - N/A
+    0,    //  0x29 - N/A
+    0,    //  0x2A - N/A
+    0,    //  0x2B - N/A
+    17,   //  0x2C - HCI_Synchronous_Connection_Complete Event
+    9,    //  0x2D - HCI_Synchronous_Connection_Changed Event
+    11,   //  0x2E - HCI_Sniff_Subrating Event
+    255,  //  0x2F - HCI_Extended_Inquiry_Result Event
+    3,    //  0x30 - HCI_Encryption_Key_Refresh_Complete Event
+    6,    //  0x31 - HCI_IO_Capability_Request Event
+    9,    //  0x32 - HCI_IO_Capability_Response Event
+    10,   //  0x33 - HCI_User_Confirmation_Request Event
+    6,    //  0x34 - HCI_User_Passkey_Request Event
+    6,    //  0x35 - HCI_Remote_OOB_Data_Request Event
+    7,    //  0x36 - HCI_Simple_Pairing_Complete Event
+    0,    //  0x37 - N/A
+    4,    //  0x38 - HCI_Link_Supervision_Timeout_Changed Event
+    2,    //  0x39 - HCI_Enhanced_Flush_Complete Event
+    0,    //  0x3A - N/A
+    10,   //  0x3B - HCI_User_Passkey_Notification Event
+    7,    //  0x3C - HCI_Keypress_Notification Event
+    14,   //  0x3D - HCI_Remote_Host_Supported_Features_Notification Event
+    0,    //  0x3E - LE Meta event
+    0,    //  0x3F - N/A
+    2,    //  0x40 - HCI_Physical_Link_Complete Event
+    1,    //  0x41 - HCI_Channel_Selected Event
+    3,    //  0x42 - HCI_Disconnection_Physical_Link_Complete Event
+    2,    //  0x43 - HCI_Physical_Link_Loss_Early_Warning Event
+    1,    //  0x44 - HCI_Physical_Link_Recovery Event
+    5,    //  0x45 - HCI_Logical_Link_Complete Event
+    4,    //  0x46 - HCI_Disconnection_Logical_Link_Complete Event
+    3,    //  0x47 - HCI_Flow_Spec_Modify_Complete Event
+    9,    //  0x48 - HCI_Number_Of_Completed_Data_Blocks Event (Num_Handles = 1)
+    2,    //  0x49 - HCI_AMP_Start_Test Event
+    2,    //  0x4A - HCI_AMP_Test_End Event
+    18,   //  0x4B - HCI_AMP_Receiver_Report Event
+    3,    //  0x4C - HCI_Short_Range_Mode_Change_Complete Event
+    2,    //  0x4D - HCI_AMP_Status_Change Event
+    9,    //  0x4E - HCI_Triggered_Clock_Capture Event
+    1,    //  0x4F - HCI_Synchronization_Train_Complete Event
+    29,   //  0x50 - HCI_Synchronization_Train_Received Event
+    18,   //  0x51 - HCI_Connectionless_Slave_Broadcast_Receive Event
+          //  (Data_Length = 0)
+    7,    //  0x52 - HCI_Connectionless_Slave_Broadcast_Timeout Event
+    7,    //  0x53 - HCI_Truncated_Page_Complete Event
+    0,    //  0x54 - HCI_Slave_Page_Response_Timeout Event
+    10,   //  0x55 - HCI_Connectionless_Slave_Broadcast_Channel_Map_Change Event
+    4,    //  0x56 - HCI_Inquiry_Response_Notification Event
+    2,    //  0x57 - HCI_Authenticated_Payload_Timeout_Expired Event
+    8,    //  0x58 - HCI_SAM_Status_Change Event
+    0,    //  0x59 - N/A
+    0,    //  0x5A - N/A
+    0,    //  0x5B - N/A
+    0,    //  0x5C - N/A
+    0,    //  0x5D - N/A
+    0,    //  0x5E - N/A
+    0,    //  0x5F - N/A
+    0,    //  0x60 - N/A
+    0,    //  0x61 - N/A
+    0,    //  0x62 - N/A
+    0,    //  0x63 - N/A
+    0,    //  0x64 - N/A
+    0,    //  0x65 - N/A
+    0,    //  0x66 - N/A
+    0,    //  0x67 - N/A
+    0,    //  0x68 - N/A
+    0,    //  0x69 - N/A
+    0,    //  0x6A - N/A
+    0,    //  0x6B - N/A
+    0,    //  0x6C - N/A
+    0,    //  0x6D - N/A
+    0,    //  0x6E - N/A
+    0,    //  0x6F - N/A
+    0,    //  0x70 - N/A
+    0,    //  0x71 - N/A
+    0,    //  0x72 - N/A
+    0,    //  0x73 - N/A
+    0,    //  0x74 - N/A
+    0,    //  0x75 - N/A
+    0,    //  0x76 - N/A
+    0,    //  0x77 - N/A
+    0,    //  0x78 - N/A
+    0,    //  0x79 - N/A
+    0,    //  0x7A - N/A
+    0,    //  0x7B - N/A
+    0,    //  0x7C - N/A
+    0,    //  0x7D - N/A
+    0,    //  0x7E - N/A
+    0,    //  0x7F - N/A
+    0,    //  0x80 - N/A
+    0,    //  0x81 - N/A
+    0,    //  0x82 - N/A
+    0,    //  0x83 - N/A
+    0,    //  0x84 - N/A
+    0,    //  0x85 - N/A
+    0,    //  0x86 - N/A
+    0,    //  0x87 - N/A
+    0,    //  0x88 - N/A
+    0,    //  0x89 - N/A
+    0,    //  0x8A - N/A
+    0,    //  0x8B - N/A
+    0,    //  0x8C - N/A
+    0,    //  0x8D - N/A
+    0,    //  0x8E - N/A
+    0,    //  0x8F - N/A
+    0,    //  0x90 - N/A
+    0,    //  0x91 - N/A
+    0,    //  0x92 - N/A
+    0,    //  0x93 - N/A
+    0,    //  0x94 - N/A
+    0,    //  0x95 - N/A
+    0,    //  0x96 - N/A
+    0,    //  0x97 - N/A
+    0,    //  0x98 - N/A
+    0,    //  0x99 - N/A
+    0,    //  0x9A - N/A
+    0,    //  0x9B - N/A
+    0,    //  0x9C - N/A
+    0,    //  0x9D - N/A
+    0,    //  0x9E - N/A
+    0,    //  0x9F - N/A
+    0,    //  0xA0 - N/A
+    0,    //  0xA1 - N/A
+    0,    //  0xA2 - N/A
+    0,    //  0xA3 - N/A
+    0,    //  0xA4 - N/A
+    0,    //  0xA5 - N/A
+    0,    //  0xA6 - N/A
+    0,    //  0xA7 - N/A
+    0,    //  0xA8 - N/A
+    0,    //  0xA9 - N/A
+    0,    //  0xAA - N/A
+    0,    //  0xAB - N/A
+    0,    //  0xAC - N/A
+    0,    //  0xAD - N/A
+    0,    //  0xAE - N/A
+    0,    //  0xAF - N/A
+    0,    //  0xB0 - N/A
+    0,    //  0xB1 - N/A
+    0,    //  0xB2 - N/A
+    0,    //  0xB3 - N/A
+    0,    //  0xB4 - N/A
+    0,    //  0xB5 - N/A
+    0,    //  0xB6 - N/A
+    0,    //  0xB7 - N/A
+    0,    //  0xB8 - N/A
+    0,    //  0xB9 - N/A
+    0,    //  0xBA - N/A
+    0,    //  0xBB - N/A
+    0,    //  0xBC - N/A
+    0,    //  0xBD - N/A
+    0,    //  0xBE - N/A
+    0,    //  0xBF - N/A
+    0,    //  0xC0 - N/A
+    0,    //  0xC1 - N/A
+    0,    //  0xC2 - N/A
+    0,    //  0xC3 - N/A
+    0,    //  0xC4 - N/A
+    0,    //  0xC5 - N/A
+    0,    //  0xC6 - N/A
+    0,    //  0xC7 - N/A
+    0,    //  0xC8 - N/A
+    0,    //  0xC9 - N/A
+    0,    //  0xCA - N/A
+    0,    //  0xCB - N/A
+    0,    //  0xCC - N/A
+    0,    //  0xCD - N/A
+    0,    //  0xCE - N/A
+    0,    //  0xCF - N/A
+    0,    //  0xD0 - N/A
+    0,    //  0xD1 - N/A
+    0,    //  0xD2 - N/A
+    0,    //  0xD3 - N/A
+    0,    //  0xD4 - N/A
+    0,    //  0xD5 - N/A
+    0,    //  0xD6 - N/A
+    0,    //  0xD7 - N/A
+    0,    //  0xD8 - N/A
+    0,    //  0xD9 - N/A
+    0,    //  0xDA - N/A
+    0,    //  0xDB - N/A
+    0,    //  0xDC - N/A
+    0,    //  0xDD - N/A
+    0,    //  0xDE - N/A
+    0,    //  0xDF - N/A
+    0,    //  0xE0 - N/A
+    0,    //  0xE1 - N/A
+    0,    //  0xE2 - N/A
+    0,    //  0xE3 - N/A
+    0,    //  0xE4 - N/A
+    0,    //  0xE5 - N/A
+    0,    //  0xE6 - N/A
+    0,    //  0xE7 - N/A
+    0,    //  0xE8 - N/A
+    0,    //  0xE9 - N/A
+    0,    //  0xEA - N/A
+    0,    //  0xEB - N/A
+    0,    //  0xEC - N/A
+    0,    //  0xED - N/A
+    0,    //  0xEE - N/A
+    0,    //  0xEF - N/A
+    0,    //  0xF0 - N/A
+    0,    //  0xF1 - N/A
+    0,    //  0xF2 - N/A
+    0,    //  0xF3 - N/A
+    0,    //  0xF4 - N/A
+    0,    //  0xF5 - N/A
+    0,    //  0xF6 - N/A
+    0,    //  0xF7 - N/A
+    0,    //  0xF8 - N/A
+    0,    //  0xF9 - N/A
+    0,    //  0xFA - N/A
+    0,    //  0xFB - N/A
+    0,    //  0xFC - N/A
+    0,    //  0xFD - N/A
+    0,    //  0xFE - N/A
+    0,    //  0xFF - HCI_Vendor_Specific Event
+};
diff --git a/stack/include/hcidefs.h b/stack/include/hcidefs.h
index 22df8af..088f2b3 100644
--- a/stack/include/hcidefs.h
+++ b/stack/include/hcidefs.h
@@ -1323,6 +1323,8 @@
 
 #define HCI_FEATURE_BYTES_PER_PAGE 8
 
+#define HCI_EXT_FEATURES_SUCCESS_EVT_LEN 13
+
 #define HCI_FEATURES_KNOWN(x) \
   (((x)[0] | (x)[1] | (x)[2] | (x)[3] | (x)[4] | (x)[5] | (x)[6] | (x)[7]) != 0)
 
diff --git a/stack/include/l2c_api.h b/stack/include/l2c_api.h
index 6bea12f..7e9d194 100644
--- a/stack/include/l2c_api.h
+++ b/stack/include/l2c_api.h
@@ -85,28 +85,6 @@
 #define L2CAP_FLUSH_CHANS_ALL 0xffff
 #define L2CAP_FLUSH_CHANS_GET 0x0000
 
-/* special CID for Multi-AV for reporting congestion */
-#define L2CAP_MULTI_AV_CID 0
-
-/* length of the HCI header block */
-/* HCI header(4) + SNK count(1) + FCR bits(1) + AV data length(2) */
-#define L2CAP_MULTI_AV_HCI_HDR_LEN 8
-
-/* length of padding for 4 bytes align */
-#define L2CAP_MULTI_AV_PADDING_LEN 2
-
-/* length of the HCI header block with padding for FCR */
-/* HCI header(4) + SNK count(1) + FCR bits(1) + AV data length(2) + padding(2)
- */
-#define L2CAP_MULTI_AV_HCI_HDR_LEN_WITH_PADDING 10
-
-/* length of the L2CAP header block */
-/* HCI header(4) + L2CAP header(4) + padding(4) or control word(2) + FCS(2) */
-#define L2CAP_MULTI_AV_L2C_HDR_LEN 12
-
-/* definition used for L2CA_SetDesireRole */
-#define L2CAP_ROLE_SLAVE HCI_ROLE_SLAVE
-#define L2CAP_ROLE_MASTER HCI_ROLE_MASTER
 /* set this bit to allow switch at create conn */
 #define L2CAP_ROLE_ALLOW_SWITCH 0x80
 /* set this bit to disallow switch at create conn */
@@ -117,11 +95,9 @@
  */
 #define L2CAP_FCR_CHAN_OPT_BASIC (1 << L2CAP_FCR_BASIC_MODE)
 #define L2CAP_FCR_CHAN_OPT_ERTM (1 << L2CAP_FCR_ERTM_MODE)
-#define L2CAP_FCR_CHAN_OPT_STREAM (1 << L2CAP_FCR_STREAM_MODE)
 
-#define L2CAP_FCR_CHAN_OPT_ALL_MASK                     \
-  (L2CAP_FCR_CHAN_OPT_BASIC | L2CAP_FCR_CHAN_OPT_ERTM | \
-   L2CAP_FCR_CHAN_OPT_STREAM)
+#define L2CAP_FCR_CHAN_OPT_ALL_MASK \
+  (L2CAP_FCR_CHAN_OPT_BASIC | L2CAP_FCR_CHAN_OPT_ERTM)
 
 /* Validity check for PSM.  PSM values must be odd.  Also, all PSM values must
  * be assigned such that the least significant bit of the most sigificant
@@ -347,7 +323,7 @@
  *
  ******************************************************************************/
 extern uint16_t L2CA_Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                              bool enable_snoop);
+                              bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info);
 
 /*******************************************************************************
  *
@@ -511,14 +487,6 @@
 extern bool L2CA_GetPeerLECocConfig(uint16_t lcid,
                                     tL2CAP_LE_CFG_INFO* peer_cfg);
 
-// This function sets the callback routines for the L2CAP connection referred to
-// by |local_cid|. The callback routines can only be modified for outgoing
-// connections established by |L2CA_ConnectReq| or accepted incoming
-// connections. |callbacks| must not be NULL. This function returns true if the
-// callbacks could be updated, false if not (e.g. |local_cid| was not found).
-bool L2CA_SetConnectionCallbacks(uint16_t local_cid,
-                                 const tL2CAP_APPL_INFO* callbacks);
-
 /*******************************************************************************
  *
  * Function         L2CA_ErtmConnectRsp
@@ -596,11 +564,10 @@
 extern uint8_t L2CA_DataWrite(uint16_t cid, BT_HDR* p_data);
 
 // Given a local channel identifier, |lcid|, this function returns the bound
-// remote channel identifier, |rcid|, and the ACL link handle, |handle|. If
+// remote channel identifier, |rcid|. If
 // |lcid| is not known or is invalid, this function returns false and does not
-// modify the values pointed at by |rcid| and |handle|. |rcid| and |handle| may
-// be NULL.
-bool L2CA_GetIdentifiers(uint16_t lcid, uint16_t* rcid, uint16_t* handle);
+// modify the value pointed at by |rcid|. |rcid| may be NULL.
+bool L2CA_GetRemoteCid(uint16_t lcid, uint16_t* rcid);
 
 /*******************************************************************************
  *
@@ -675,18 +642,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_LocalLoopbackReq
- *
- * Description  This function sets up a CID for local loopback
- *
- * Returns      CID of 0 if none.
- *
- ******************************************************************************/
-extern uint16_t L2CA_LocalLoopbackReq(uint16_t psm, uint16_t handle,
-                                      const RawAddress& p_bd_addr);
-
-/*******************************************************************************
- *
  * Function     L2CA_FlushChannel
  *
  * Description  This function flushes none, some or all buffers queued up
@@ -716,31 +671,6 @@
 
 /*******************************************************************************
  *
- * Function         L2CA_FlowControl
- *
- * Description      Higher layers call this function to flow control a channel.
- *
- *                  data_enabled - true data flows, false data is stopped
- *
- * Returns          true if valid channel, else false
- *
- ******************************************************************************/
-extern bool L2CA_FlowControl(uint16_t cid, bool data_enabled);
-
-/*******************************************************************************
- *
- * Function         L2CA_SendTestSFrame
- *
- * Description      Higher layers call this function to send a test S-frame.
- *
- * Returns          true if valid Channel, else false
- *
- ******************************************************************************/
-extern bool L2CA_SendTestSFrame(uint16_t cid, uint8_t sup_type,
-                                uint8_t back_track);
-
-/*******************************************************************************
- *
  * Function         L2CA_SetTxPriority
  *
  * Description      Sets the transmission priority for a channel. (FCR Mode)
@@ -750,8 +680,6 @@
  ******************************************************************************/
 extern bool L2CA_SetTxPriority(uint16_t cid, tL2CAP_CHNL_PRIORITY priority);
 
-typedef void(tL2CA_RESERVE_CMPL_CBACK)(void);
-
 /*******************************************************************************
  *
  * Function         L2CA_SetFlushTimeout
@@ -808,33 +736,6 @@
 
 /*******************************************************************************
  *
- *  Function         L2CA_GetBDAddrbyHandle
- *
- *  Description      Get BD address for the given HCI handle
- *
- *  Parameters:      HCI handle
- *                   BD address of the peer
- *
- *  Return value:    true if found lcb for the given handle, false otherwise
- *
- ******************************************************************************/
-extern bool L2CA_GetBDAddrbyHandle(uint16_t handle, RawAddress& bd_addr);
-
-/*******************************************************************************
- *
- *  Function         L2CA_GetChnlFcrMode
- *
- *  Description      Get the channel FCR mode
- *
- *  Parameters:      Local CID
- *
- *  Return value:    Channel mode
- *
- ******************************************************************************/
-extern uint8_t L2CA_GetChnlFcrMode(uint16_t lcid);
-
-/*******************************************************************************
- *
  *                      Fixed Channel callback prototypes
  *
  ******************************************************************************/
@@ -870,7 +771,6 @@
   tL2CA_FIXED_CHNL_CB* pL2CA_FixedConn_Cb;
   tL2CA_FIXED_DATA_CB* pL2CA_FixedData_Cb;
   tL2CA_FIXED_CONGESTION_STATUS_CB* pL2CA_FixedCong_Cb;
-  tL2CAP_FCR_OPTS fixed_chnl_opts;
 
   uint16_t default_idle_tout;
   tL2CA_TX_COMPLETE_CB*
@@ -967,37 +867,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_GetCurrentConfig
- *
- * Description  This function returns configurations of L2CAP channel
- *              pp_our_cfg : pointer of our saved configuration options
- *              p_our_cfg_bits : valid config in bitmap
- *              pp_peer_cfg: pointer of peer's saved configuration options
- *              p_peer_cfg_bits : valid config in bitmap
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-extern bool L2CA_GetCurrentConfig(uint16_t lcid, tL2CAP_CFG_INFO** pp_our_cfg,
-                                  tL2CAP_CH_CFG_BITS* p_our_cfg_bits,
-                                  tL2CAP_CFG_INFO** pp_peer_cfg,
-                                  tL2CAP_CH_CFG_BITS* p_peer_cfg_bits);
-
-/*******************************************************************************
- *
- * Function     L2CA_GetConnectionConfig
- *
- * Description  This function polulates the mtu, remote cid & lm_handle for
- *              a given local L2CAP channel
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-extern bool L2CA_GetConnectionConfig(uint16_t lcid, uint16_t* mtu,
-                                     uint16_t* rcid, uint16_t* handle);
-
-/*******************************************************************************
- *
  *  Function        L2CA_CancelBleConnectReq
  *
  *  Description     Cancel a pending connection attempt to a BLE device.
diff --git a/stack/include/l2cap_client.h b/stack/include/l2cap_client.h
deleted file mode 100644
index edb204e..0000000
--- a/stack/include/l2cap_client.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2014 Google, Inc.
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#pragma once
-
-#include <hardware/bluetooth.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-typedef struct buffer_t buffer_t;
-typedef struct l2cap_client_t l2cap_client_t;
-
-typedef struct {
-  void (*connected)(l2cap_client_t* client, void* context);
-  void (*disconnected)(l2cap_client_t* client, void* context);
-  void (*read_ready)(l2cap_client_t* client, buffer_t* packet, void* context);
-  void (*write_ready)(l2cap_client_t* client, void* context);
-} l2cap_client_callbacks_t;
-
-// Returns a new buffer with enough space for |size| bytes of L2CAP payload.
-// |size| must be greater than zero. This function returns NULL if the buffer
-// could not be allocated. The returned buffer must be freed with |buffer_free|
-// when it is no longer needed.
-buffer_t* l2cap_buffer_new(size_t size);
-
-// Creates and returns a new L2CAP client object. |callbacks| must not be NULL
-// and must specify a set of functions that should be called back when events
-// occur on the L2CAP connection. |context| may be NULL and will be passed as
-// the argument to all callbacks in |l2cap_client_callbacks_t|. The returned
-// object must be freed with |l2cap_client_free|.
-l2cap_client_t* l2cap_client_new(const l2cap_client_callbacks_t* callbacks,
-                                 void* context);
-
-// Frees the L2CAP client object allocated with |l2cap_client_new|. |client| may
-// be NULL.
-void l2cap_client_free(l2cap_client_t* client);
-
-// Attempts to connect the |client| to a peer device specified by
-// |remote_bdaddr| using the |psm| protocol specifier. This function returns
-// true if the connect operation could be started and will indicate completion
-// with either a 'connected' callback (success) or a 'disconnected' callback
-// (failure).
-//
-// This function must not be called while a connect operation is in progress or
-// while |l2cap_client_is_connected|. |client| and |remote_bdaddr| must not be
-// NULL. |psm| must be greater than zero.
-bool l2cap_client_connect(l2cap_client_t* client,
-                          const RawAddress& remote_bdaddr, uint16_t psm);
-
-// Disconnects a connected |client|. This function is asynchronous and
-// idempotent. It will indicate completion with a 'disconnected' callback.
-// |client| must not be NULL.
-void l2cap_client_disconnect(l2cap_client_t* client);
-
-// Returns true if |client| is connected and is ready to accept data written
-// to it. |client| must not be NULL.
-bool l2cap_client_is_connected(const l2cap_client_t* client);
-
-// Writes data contained in |packet| to a connected |client|. This function
-// returns true if the packet was successfully queued for delivery, false if the
-// client cannot accept more data at this time. If this function returns false,
-// the caller must wait for the 'write_ready' callback to write additional data
-// to the client. Neither |client| nor |packet| may be NULL.
-bool l2cap_client_write(l2cap_client_t* client, buffer_t* packet);
diff --git a/stack/include/ldacBT_bco_for_fluoride.h b/stack/include/ldacBT_bco_for_fluoride.h
new file mode 100644
index 0000000..9666a7c
--- /dev/null
+++ b/stack/include/ldacBT_bco_for_fluoride.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2016 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.
+ */
+#ifndef LDACBT_BCO_FOR_FLUORIDE_H__
+#define LDACBT_BCO_FOR_FLUORIDE_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef LDAC_BCO_API
+#define LDAC_BCO_API
+#endif /* LDAC_BCO_API */
+
+/* This file contains the definitions, declarations and macros for an
+ * implementation of LDAC buffer control operation.
+ */
+
+#define LDAC_BCO_ERR_NONE 0
+#define LDAC_BCO_ERR_FATAL (-1)
+
+/* LDAC BCO handle type */
+typedef struct _ldacbt_bco_handle* HANDLE_LDAC_BCO;
+
+typedef void (*decoded_data_callback_t)(uint8_t* buf, uint32_t len);
+
+/* Prepare to use LDAC BCO.
+ *  - Register a callback function for passing decoded data.
+ *  - Allocation of LDAC BCO handle.
+ *  Format
+ *      HANDLE_LDAC_BCO ldac_BCO_init(decoded_data_callback_t decode_callback);
+ *  Arguments
+ *      decoded_data_callback_t decode_callback
+ *              Callback function that outputs PCM data after decoding.
+ *              (See also a2dp_codec_api.h)
+ *  Return value
+ *      HANDLE_LDAC_BCO for success, NULL for failure.
+ */
+LDAC_BCO_API HANDLE_LDAC_BCO
+ldac_BCO_init(decoded_data_callback_t decode_callback);
+
+/* End LDAC BCO.
+ *  - Release of LDAC BCO handle.
+ *  Format
+ *      int32_t ldac_BCO_cleanup(HANDLE_LDAC_BCO hLdacBco);
+ *  Arguments
+ *      HANDLE_LDAC_BCO  hLdacBco    LDAC BCO handle.
+ *  Return value
+ *      int32_t : Processing result.
+ *              LDAC_BCO_ERR_NONE:Successful completion
+ *              LDAC_BCO_ERR_FATAL:Error
+ *  Note
+ *      The function ldac_BCO_init() shall be called before calling this
+ *      function.
+ */
+LDAC_BCO_API int32_t ldac_BCO_cleanup(HANDLE_LDAC_BCO hLdacBco);
+
+/* Decode LDAC packets.
+ * - Perform buffer control and decode processing.
+ *  Format
+ *      int32_t ldac_BCO_decode_packet(HANDLE_LDAC_BCO hLdacBco, void *data,
+ *                                                            int32_t length);
+ * Arguments
+ *      HANDLE_LDAC_BCO  hLdacBco    LDAC BCO handle.
+ *      void *data                   LDAC packet.
+ *      int32_t length               LDAC packet size.
+ * Return value
+ *      int32_t : Processing result.
+ *              LDAC_BCO_ERR_NONE:Successful completion
+ *              LDAC_BCO_ERR_FATAL:Error
+ * Note
+ *      The function ldac_BCO_init() shall be called before calling this
+ *      function.
+ */
+LDAC_BCO_API int32_t ldac_BCO_decode_packet(HANDLE_LDAC_BCO hLdacBco,
+                                            void* data, int32_t length);
+
+/* Start decoding process.
+ *  - Start or resume decoder thread.
+ *  Format
+ *      int32_t ldac_BCO_start(HANDLE_LDAC_BCO hLdacBco);
+ *  Arguments
+ *      HANDLE_LDAC_BCO  hLdacBco    LDAC BCO handle.
+ *  Return value
+ *      int32_t : Processing result.
+ *              LDAC_BCO_ERR_NONE:Successful completion
+ *              LDAC_BCO_ERR_FATAL:Error
+ *  Note
+ *      The function ldac_BCO_init() shall be called before calling this
+ *      function.
+ */
+LDAC_BCO_API int32_t ldac_BCO_start(HANDLE_LDAC_BCO hLdacBco);
+
+/* Suspend decoding process.
+ *  - Suspend the decoder thread.
+ *  Format
+ *      int32_t ldac_BCO_suspend(HANDLE_LDAC_BCO hLdacBco);
+ *  Arguments
+ *      HANDLE_LDAC_BCO  hLdacBco    LDAC BCO handle.
+ *  Return value
+ *      int32_t : Processing result.
+ *              LDAC_BCO_ERR_NONE:Successful completion
+ *              LDAC_BCO_ERR_FATAL:Error
+ *  Note
+ *      The function ldac_BCO_init() shall be called before calling this
+ *      function.
+ */
+LDAC_BCO_API int32_t ldac_BCO_suspend(HANDLE_LDAC_BCO hLdacBco);
+
+/* Configure codec information.
+ *  - Set sample rate, bits/sample and channel mode.
+ *  Format
+ *      int32_t ldac_BCO_configure(HANDLE_LDAC_BCO hLdacBco,
+ *              int32_t sample_rate, int32_t bits_per_sample,
+ *              int32_t channel_mode);
+ *  Arguments
+ *      HANDLE_LDAC_BCO  hLdacBco    LDAC BCO handle.
+ *      int32_t sample_rate          sample rate.
+ *      int32_t bits_per_sample      bits/sample.
+ *      int32_t channel_mode         channel mode.
+ *  Return value
+ *      int32_t : Processing result.
+ *              LDAC_BCO_ERR_NONE:Successful completion
+ *              LDAC_BCO_ERR_FATAL:Error
+ *  Note
+ *      The function ldac_BCO_init() shall be called before calling this
+ *      function.
+ */
+LDAC_BCO_API int32_t ldac_BCO_configure(HANDLE_LDAC_BCO hLdacBco,
+                                        int32_t sample_rate,
+                                        int32_t bits_per_sample,
+                                        int32_t channel_mode);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LDACBT_BCO_FOR_FLUORIDE_H__ */
diff --git a/stack/include/port_api.h b/stack/include/port_api.h
index b0a7e63..daea3d4 100644
--- a/stack/include/port_api.h
+++ b/stack/include/port_api.h
@@ -35,47 +35,27 @@
  * set settings request, or to the application in the set settings indication.
 */
 typedef struct {
-#define PORT_BAUD_RATE_2400 0x00
-#define PORT_BAUD_RATE_4800 0x01
-#define PORT_BAUD_RATE_7200 0x02
 #define PORT_BAUD_RATE_9600 0x03
-#define PORT_BAUD_RATE_19200 0x04
-#define PORT_BAUD_RATE_38400 0x05
-#define PORT_BAUD_RATE_57600 0x06
-#define PORT_BAUD_RATE_115200 0x07
-#define PORT_BAUD_RATE_230400 0x08
 
   uint8_t baud_rate;
 
-#define PORT_5_BITS 0x00
-#define PORT_6_BITS 0x01
-#define PORT_7_BITS 0x02
 #define PORT_8_BITS 0x03
 
   uint8_t byte_size;
 
 #define PORT_ONESTOPBIT 0x00
-#define PORT_ONE5STOPBITS 0x01
   uint8_t stop_bits;
 
 #define PORT_PARITY_NO 0x00
-#define PORT_PARITY_YES 0x01
   uint8_t parity;
 
 #define PORT_ODD_PARITY 0x00
-#define PORT_EVEN_PARITY 0x01
-#define PORT_MARK_PARITY 0x02
-#define PORT_SPACE_PARITY 0x03
 
   uint8_t parity_type;
 
 #define PORT_FC_OFF 0x00
-#define PORT_FC_XONXOFF_ON_INPUT 0x01
-#define PORT_FC_XONXOFF_ON_OUTPUT 0x02
 #define PORT_FC_CTS_ON_INPUT 0x04
 #define PORT_FC_CTS_ON_OUTPUT 0x08
-#define PORT_FC_DSR_ON_INPUT 0x10
-#define PORT_FC_DSR_ON_OUTPUT 0x20
 
   uint8_t fc_type;
 
@@ -133,18 +113,6 @@
 #define PORT_EV_FCS 0x00020000
 
 /*
- * To register for events application should provide bitmask with
- * corresponding bit set
-*/
-
-#define PORT_MASK_ALL                                                  \
-  (PORT_EV_RXCHAR | PORT_EV_TXEMPTY | PORT_EV_CTS | PORT_EV_DSR |      \
-   PORT_EV_RLSD | PORT_EV_BREAK | PORT_EV_ERR | PORT_EV_RING |         \
-   PORT_EV_CONNECT_ERR | PORT_EV_DSRS | PORT_EV_CTSS | PORT_EV_RLSDS | \
-   PORT_EV_RXFLAG | PORT_EV_TXCHAR | PORT_EV_OVERRUN | PORT_EV_FC |    \
-   PORT_EV_FCS | PORT_EV_CONNECTED)
-
-/*
  * Define port result codes
 */
 #define PORT_SUCCESS 0
@@ -267,21 +235,6 @@
  ******************************************************************************/
 int PORT_ClearKeepHandleFlag(uint16_t port_handle);
 
-/*******************************************************************************
- *
- * Function         PORT_SetEventCallback
- *
- * Description      Set event data callback the specified connection.
- *
- * Parameters:      handle       - Handle of the port returned in the Open
- *                  p_callback   - address of the callback function which should
- *                                 be called from the RFCOMM when a data
- *                                 packet is received.
- *
- ******************************************************************************/
-extern int PORT_SetDataCallback(uint16_t port_handle,
-                                tPORT_DATA_CALLBACK* p_cb);
-
 extern int PORT_SetDataCOCallback(uint16_t port_handle,
                                   tPORT_DATA_CO_CALLBACK* p_port_cb);
 /*******************************************************************************
@@ -341,18 +294,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_GetRxQueueCnt
- *
- * Description      This function return number of buffers on the rx queue.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_rx_queue_count - Pointer to return queue count in.
- *
- ******************************************************************************/
-extern int PORT_GetRxQueueCnt(uint16_t handle, uint16_t* p_rx_queue_count);
-
-/*******************************************************************************
- *
  * Function         PORT_GetState
  *
  * Description      This function is called to fill tPORT_STATE structure
@@ -367,43 +308,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Control
- *
- * Description      This function directs a specified connection to pass control
- *                  control information to the peer device.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  signal     - specify the function to be passed
- *
- ******************************************************************************/
-#define PORT_SET_DTRDSR 0x01
-#define PORT_CLR_DTRDSR 0x02
-#define PORT_SET_CTSRTS 0x03
-#define PORT_CLR_CTSRTS 0x04
-#define PORT_SET_RI 0x05  /* DCE only */
-#define PORT_CLR_RI 0x06  /* DCE only */
-#define PORT_SET_DCD 0x07 /* DCE only */
-#define PORT_CLR_DCD 0x08 /* DCE only */
-#define PORT_BREAK 0x09   /* Break event */
-
-extern int PORT_Control(uint16_t handle, uint8_t signal);
-
-/*******************************************************************************
- *
- * Function         PORT_FlowControl
- *
- * Description      This function directs a specified connection to pass
- *                  flow control message to the peer device.  Enable flag passed
- *                  shows if port can accept more data.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  enable     - enables data flow
- *
- ******************************************************************************/
-extern int PORT_FlowControl(uint16_t handle, bool enable);
-
-/*******************************************************************************
- *
  * Function         PORT_FlowControl_MaxCredit
  *
  * Description      This function directs a specified connection to pass
@@ -417,20 +321,6 @@
  ******************************************************************************/
 extern int PORT_FlowControl_MaxCredit(uint16_t handle, bool enable);
 
-/*******************************************************************************
- *
- * Function         PORT_GetModemStatus
- *
- * Description      This function retrieves modem control signals.  Normally
- *                  application will call this function after a callback
- *                  function is called with notification that one of signals
- *                  has been changed.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                               callback.
- *                  p_signal   - specify the pointer to control signals info
- *
- ******************************************************************************/
 #define PORT_DTRDSR_ON 0x01
 #define PORT_CTSRTS_ON 0x02
 #define PORT_RING_ON 0x04
@@ -447,103 +337,12 @@
   (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON)
 #define PORT_DUN_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON)
 
-extern int PORT_GetModemStatus(uint16_t handle, uint8_t* p_control_signal);
-
-/*******************************************************************************
- *
- * Function         PORT_ClearError
- *
- * Description      This function retreives information about a communications
- *                  error and reports current status of a connection.  The
- *                  function should be called when an error occures to clear
- *                  the connection error flag and to enable additional read
- *                  and write operations.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_errors   - pointer of the variable to receive error codes
- *                  p_status   - pointer to the tPORT_STATUS structur to receive
- *                               connection status
- *
- ******************************************************************************/
-
 #define PORT_ERR_BREAK 0x01   /* Break condition occured on the peer device */
 #define PORT_ERR_OVERRUN 0x02 /* Overrun is reported by peer device */
 #define PORT_ERR_FRAME 0x04   /* Framing error reported by peer device */
 #define PORT_ERR_RXOVER 0x08  /* Input queue overflow occured */
 #define PORT_ERR_TXFULL 0x10  /* Output queue overflow occured */
 
-typedef struct {
-#define PORT_FLAG_CTS_HOLD 0x01  /* Tx is waiting for CTS signal */
-#define PORT_FLAG_DSR_HOLD 0x02  /* Tx is waiting for DSR signal */
-#define PORT_FLAG_RLSD_HOLD 0x04 /* Tx is waiting for RLSD signal */
-
-  uint16_t flags;
-  uint16_t in_queue_size;  /* Number of bytes in the input queue */
-  uint16_t out_queue_size; /* Number of bytes in the output queue */
-  uint16_t mtu_size;       /* peer MTU size */
-} tPORT_STATUS;
-
-extern int PORT_ClearError(uint16_t handle, uint16_t* p_errors,
-                           tPORT_STATUS* p_status);
-
-/*******************************************************************************
- *
- * Function         PORT_SendError
- *
- * Description      This function send a communications error to the peer device
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  errors     - receive error codes
- *
- ******************************************************************************/
-extern int PORT_SendError(uint16_t handle, uint8_t errors);
-
-/*******************************************************************************
- *
- * Function         PORT_GetQueueStatus
- *
- * Description      This function reports current status of a connection.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_status   - pointer to the tPORT_STATUS structur to receive
- *                               connection status
- *
- ******************************************************************************/
-extern int PORT_GetQueueStatus(uint16_t handle, tPORT_STATUS* p_status);
-
-/*******************************************************************************
- *
- * Function         PORT_Purge
- *
- * Description      This function discards all the data from the output or
- *                  input queues of the specified connection.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  purge_flags - specify the action to take.
- *
- ******************************************************************************/
-#define PORT_PURGE_TXCLEAR 0x01
-#define PORT_PURGE_RXCLEAR 0x02
-
-extern int PORT_Purge(uint16_t handle, uint8_t purge_flags);
-
-/*******************************************************************************
- *
- * Function         PORT_Read
- *
- * Description      This function returns the pointer to the buffer received
- *                  from the peer device.  Normally application will call this
- *                  function after receiving PORT_EVT_RXCHAR event.
- *                  Application calling this function is responsible to free
- *                  buffer returned.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                                callback.
- *                  pp_buf      - pointer to address of buffer with data,
- *
- ******************************************************************************/
-extern int PORT_Read(uint16_t handle, BT_HDR** pp_buf);
-
 /*******************************************************************************
  *
  * Function         PORT_ReadData
@@ -563,19 +362,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Write
- *
- * Description      This function to send BT buffer to the peer device.
- *                  Application should not free the buffer.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_buf       - pointer to the buffer with data,
- *
- ******************************************************************************/
-extern int PORT_Write(uint16_t handle, BT_HDR* p_buf);
-
-/*******************************************************************************
- *
  * Function         PORT_WriteData
  *
  * Description      This function is called from the legacy application to
@@ -604,19 +390,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Test
- *
- * Description      Application can call this function to send RFCOMM Test frame
- *
- * Parameters:      handle      - Handle returned in the RFCOMM_CreateConnection
- *                  p_data      - Data area
- *                  max_len     - Byte count requested
- *
- ******************************************************************************/
-extern int PORT_Test(uint16_t handle, uint8_t* p_data, uint16_t len);
-
-/*******************************************************************************
- *
  * Function         RFCOMM_Init
  *
  * Description      This function is called to initialize RFCOMM layer
diff --git a/stack/include/profiles_api.h b/stack/include/profiles_api.h
index f91826d..f533deb 100644
--- a/stack/include/profiles_api.h
+++ b/stack/include/profiles_api.h
@@ -19,57 +19,12 @@
 #ifndef PROFILES_API_H
 #define PROFILES_API_H
 
-#include "bt_target.h"
-#include "btm_api.h"
-
 /*****************************************************************************
  *  Constants
  ****************************************************************************/
 #define BT_PASS 0 /* Used for general successful function returns */
 
 /*** Port entity passes back 8 bit errors; will use upper byte offset ***/
-#define PORT_ERR_GRP 0x0000  /* base offset for port entity */
 #define GAP_ERR_GRP 0x0100   /* base offset for GAP profile */
-#define SPP_ERR_GRP 0x0200   /* base offset for serial port profile */
-#define HCRP_ERR_GRP 0x0300  /* base offset for HCRP */
-#define HCRPM_ERR_GRP 0x0400 /* base offset for HCRPM */
-
-/* #define HSP2_ERR_GRP 0x0F00 */
-
-/* security level definitions (tBT_SECURITY) */
-#define BT_USE_DEF_SECURITY 0
-#define BT_SEC_MODE_NONE BTM_SEC_MODE_NONE
-#define BT_SEC_MODE_SERVICE BTM_SEC_MODE_SERVICE
-#define BT_SEC_MODE_LINK BTM_SEC_MODE_LINK
-
-/* security mask definitions (tBT_SECURITY) */
-/* The following definitions are OR'd together to form the security
- * requirements */
-/* Inbound call requires authorization */
-#define BT_SEC_IN_AUTHORIZE BTM_SEC_IN_AUTHORIZE
-/* Inbound call requires authentication */
-#define BT_SEC_IN_AUTHENTICATE BTM_SEC_IN_AUTHENTICATE
-/* Inbound call requires encryption */
-#define BT_SEC_IN_ENCRYPT BTM_SEC_IN_ENCRYPT
-/* Outbound call requires authorization */
-#define BT_SEC_OUT_AUTHORIZE BTM_SEC_OUT_AUTHORIZE
-/* Outbound call requires authentication */
-#define BT_SEC_OUT_AUTHENTICATE BTM_SEC_OUT_AUTHENTICATE
-/* Outbound call requires encryption */
-#define BT_SEC_OUT_ENCRYPT BTM_SEC_OUT_ENCRYPT
-
-/*****************************************************************************
- *  Type Definitions
- ****************************************************************************/
-
-/*
- * Security Definitions
- *      This following definitions are used to indicate the security
- *      requirements for a service.
-*/
-typedef struct {
-  uint8_t level;
-  uint8_t mask;
-} tBT_SECURITY;
 
 #endif /* PROFILES_API_H */
diff --git a/stack/include/sdp_api.h b/stack/include/sdp_api.h
index 7752fd6..3bc1a30 100644
--- a/stack/include/sdp_api.h
+++ b/stack/include/sdp_api.h
@@ -83,9 +83,6 @@
   tSDP_DR_DATA data;
 } tSDP_DATA;
 
-/* Define a callback function for when discovery result is received. */
-typedef void(tSDP_DISC_RES_CB)(uint16_t event, tSDP_DATA* p_data);
-
 /* Define a structure to hold the discovered service information. */
 typedef struct {
   union {
@@ -240,21 +237,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_FindAttributeInDb
- *
- * Description      This function queries an SDP database for a specific
- *                  attribute. If the p_start_rec pointer is NULL, it looks from
- *                  the beginning of the database, else it continues from the
- *                  next record after p_start_rec.
- *
- * Returns          Pointer to matching record, or NULL
- *
- ******************************************************************************/
-tSDP_DISC_REC* SDP_FindAttributeInDb(tSDP_DISCOVERY_DB* p_db, uint16_t attr_id,
-                                     tSDP_DISC_REC* p_start_rec);
-
-/*******************************************************************************
- *
  * Function         SDP_FindAttributeInRec
  *
  * Description      This function searches an SDP discovery record for a
@@ -348,20 +330,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_FindAddProtoListsElemInRec
- *
- * Description      This function looks at a specific discovery record for a
- *                  protocol list element.
- *
- * Returns          true if found, false if not
- *                  If found, the passed protocol list element is filled in.
- *
- ******************************************************************************/
-bool SDP_FindAddProtoListsElemInRec(tSDP_DISC_REC* p_rec, uint16_t layer_uuid,
-                                    tSDP_PROTOCOL_ELEM* p_elem);
-
-/*******************************************************************************
- *
  * Function         SDP_FindProfileVersionInRec
  *
  * Description      This function looks at a specific discovery record for the
@@ -410,21 +378,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_ReadRecord
- *
- * Description      This function is called to get the raw data of the record
- *                  with the given handle from the database.
- *
- * Returns          -1, if the record is not found.
- *                  Otherwise, the offset (0 or 1) to start of data in p_data.
- *
- *                  The size of data copied into p_data is in *p_data_len.
- *
- ******************************************************************************/
-int32_t SDP_ReadRecord(uint32_t handle, uint8_t* p_data, int32_t* p_data_len);
-
-/*******************************************************************************
- *
  * Function         SDP_AddAttribute
  *
  * Description      This function is called to add an attribute to a record.
diff --git a/stack/l2cap/l2c_api.cc b/stack/l2cap/l2c_api.cc
index 90aab52..394a8fa 100644
--- a/stack/l2cap/l2c_api.cc
+++ b/stack/l2cap/l2c_api.cc
@@ -61,9 +61,10 @@
  *
  ******************************************************************************/
 uint16_t L2CA_Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                       bool enable_snoop) {
+                       bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info) {
   if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_Register(psm, p_cb_info, enable_snoop);
+    return bluetooth::shim::L2CA_Register(psm, p_cb_info, enable_snoop,
+                                          p_ertm_info);
   }
 
   tL2C_RCB* p_rcb;
@@ -721,48 +722,6 @@
   return true;
 }
 
-bool L2CA_SetConnectionCallbacks(uint16_t local_cid,
-                                 const tL2CAP_APPL_INFO* callbacks) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_SetConnectionCallbacks(local_cid, callbacks);
-  }
-
-  CHECK(callbacks != NULL);
-  CHECK(callbacks->pL2CA_ConnectInd_Cb == NULL);
-  CHECK(callbacks->pL2CA_ConnectCfm_Cb != NULL);
-  CHECK(callbacks->pL2CA_ConfigInd_Cb != NULL);
-  CHECK(callbacks->pL2CA_ConfigCfm_Cb != NULL);
-  CHECK(callbacks->pL2CA_DisconnectInd_Cb != NULL);
-  CHECK(callbacks->pL2CA_DisconnectCfm_Cb != NULL);
-  CHECK(callbacks->pL2CA_CongestionStatus_Cb != NULL);
-  CHECK(callbacks->pL2CA_DataInd_Cb != NULL);
-  CHECK(callbacks->pL2CA_TxComplete_Cb != NULL);
-
-  tL2C_CCB* channel_control_block = l2cu_find_ccb_by_cid(NULL, local_cid);
-  if (!channel_control_block) {
-    LOG_ERROR(LOG_TAG,
-              "%s no channel control block found for L2CAP LCID=0x%04x.",
-              __func__, local_cid);
-    return false;
-  }
-
-  // We're making a connection-specific registration control block so we check
-  // if we already have a private one allocated to us on the heap. If not, we
-  // make a new allocation, mark it as heap-allocated, and inherit the fields
-  // from the old control block.
-  tL2C_RCB* registration_control_block = channel_control_block->p_rcb;
-  if (!channel_control_block->should_free_rcb) {
-    registration_control_block = (tL2C_RCB*)osi_calloc(sizeof(tL2C_RCB));
-
-    *registration_control_block = *channel_control_block->p_rcb;
-    channel_control_block->p_rcb = registration_control_block;
-    channel_control_block->should_free_rcb = true;
-  }
-
-  registration_control_block->api = *callbacks;
-  return true;
-}
-
 /*******************************************************************************
  *
  * Function         L2CA_ConnectRsp
@@ -1033,16 +992,15 @@
   return (true);
 }
 
-bool L2CA_GetIdentifiers(uint16_t lcid, uint16_t* rcid, uint16_t* handle) {
+bool L2CA_GetRemoteCid(uint16_t lcid, uint16_t* rcid) {
   if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_GetIdentifiers(lcid, rcid, handle);
+    return bluetooth::shim::L2CA_GetRemoteCid(lcid, rcid);
   }
 
   tL2C_CCB* control_block = l2cu_find_ccb_by_cid(NULL, lcid);
   if (!control_block) return false;
 
   if (rcid) *rcid = control_block->remote_cid;
-  if (handle) *handle = control_block->p_lcb->handle;
 
   return true;
 }
@@ -1207,67 +1165,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_LocalLoopbackReq
- *
- * Description  This function sets up a CID for local loopback
- *
- * Returns      CID of 0 if none.
- *
- ******************************************************************************/
-uint16_t L2CA_LocalLoopbackReq(uint16_t psm, uint16_t handle,
-                               const RawAddress& p_bd_addr) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_LocalLoopbackReq(psm, handle, p_bd_addr);
-  }
-
-  tL2C_LCB* p_lcb;
-  tL2C_CCB* p_ccb;
-  tL2C_RCB* p_rcb;
-
-  L2CAP_TRACE_API("L2CA_LocalLoopbackReq()  PSM: %d  Handle: 0x%04x", psm,
-                  handle);
-
-  /* Fail if we have not established communications with the controller */
-  if (!BTM_IsDeviceUp()) {
-    L2CAP_TRACE_WARNING("L2CAP loop req - BTU not ready");
-    return (0);
-  }
-
-  /* Fail if the PSM is not registered */
-  p_rcb = l2cu_find_rcb_by_psm(psm);
-  if (p_rcb == NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - no RCB for L2CA_conn_req, PSM: %d", psm);
-    return (0);
-  }
-
-  p_lcb = l2cu_allocate_lcb(p_bd_addr, false, BT_TRANSPORT_BR_EDR);
-  if (p_lcb == NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - no LCB for L2CA_conn_req");
-    return (0);
-  }
-
-  p_lcb->link_state = LST_CONNECTED;
-  p_lcb->handle = handle;
-
-  /* Allocate a channel control block */
-  p_ccb = l2cu_allocate_ccb(p_lcb, 0);
-  if (p_ccb == NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - no CCB for L2CA_conn_req");
-    return (0);
-  }
-
-  /* Save registration info */
-  p_ccb->p_rcb = p_rcb;
-  p_ccb->chnl_state = CST_OPEN;
-  p_ccb->remote_cid = p_ccb->local_cid;
-  p_ccb->config_done = CFG_DONE_MASK;
-
-  /* Return the local CID as our handle */
-  return (p_ccb->local_cid);
-}
-
-/*******************************************************************************
- *
  * Function         L2CA_SetAclPriority
  *
  * Description      Sets the transmission priority for a channel.
@@ -1289,95 +1186,6 @@
 
 /*******************************************************************************
  *
- * Function         L2CA_FlowControl
- *
- * Description      Higher layers call this function to flow control a channel.
- *
- *                  data_enabled - true data flows, false data is stopped
- *
- * Returns          true if valid channel, else false
- *
- ******************************************************************************/
-bool L2CA_FlowControl(uint16_t cid, bool data_enabled) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_FlowControl(cid, data_enabled);
-  }
-
-  tL2C_CCB* p_ccb;
-  bool on_off = !data_enabled;
-
-  L2CAP_TRACE_API("L2CA_FlowControl(%d)  CID: 0x%04x", on_off, cid);
-
-  /* Find the channel control block. We don't know the link it is on. */
-  p_ccb = l2cu_find_ccb_by_cid(NULL, cid);
-  if (p_ccb == NULL) {
-    L2CAP_TRACE_WARNING(
-        "L2CAP - no CCB for L2CA_FlowControl, CID: 0x%04x  data_enabled: %d",
-        cid, data_enabled);
-    return (false);
-  }
-
-  if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) {
-    L2CAP_TRACE_EVENT("L2CA_FlowControl()  invalid mode:%d",
-                      p_ccb->peer_cfg.fcr.mode);
-    return (false);
-  }
-  if (p_ccb->fcrb.local_busy != on_off) {
-    p_ccb->fcrb.local_busy = on_off;
-
-    if ((p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack)) {
-      if (on_off)
-        l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RNR, 0);
-      else
-        l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT);
-    }
-  }
-
-  return (true);
-}
-
-/*******************************************************************************
- *
- * Function         L2CA_SendTestSFrame
- *
- * Description      Higher layers call this function to send a test S-frame.
- *
- * Returns          true if valid Channel, else false
- *
- ******************************************************************************/
-bool L2CA_SendTestSFrame(uint16_t cid, uint8_t sup_type, uint8_t back_track) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_SendTestSFrame(cid, sup_type, back_track);
-  }
-
-  tL2C_CCB* p_ccb;
-
-  L2CAP_TRACE_API(
-      "L2CA_SendTestSFrame()  CID: 0x%04x  Type: 0x%02x  back_track: %u", cid,
-      sup_type, back_track);
-
-  /* Find the channel control block. We don't know the link it is on. */
-  p_ccb = l2cu_find_ccb_by_cid(NULL, cid);
-  if (p_ccb == NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - no CCB for L2CA_SendTestSFrame, CID: %d", cid);
-    return (false);
-  }
-
-  if ((p_ccb->chnl_state != CST_OPEN) ||
-      (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE))
-    return (false);
-
-  p_ccb->fcrb.next_seq_expected -= back_track;
-
-  l2c_fcr_send_S_frame(
-      p_ccb, (uint16_t)(sup_type & 3),
-      (uint16_t)(sup_type & (L2CAP_FCR_P_BIT | L2CAP_FCR_F_BIT)));
-
-  return (true);
-}
-
-/*******************************************************************************
- *
  * Function         L2CA_SetTxPriority
  *
  * Description      Sets the transmission priority for a channel.
@@ -1547,63 +1355,6 @@
   return true;
 }
 
-/*******************************************************************************
- *
- *  Function         L2CA_GetBDAddrbyHandle
- *
- *  Description      Get BD address for the given HCI handle
- *
- *  Parameters:      HCI handle
- *                   BD address of the peer
- *
- *  Return value:    true if found lcb for the given handle, false otherwise
- *
- ******************************************************************************/
-bool L2CA_GetBDAddrbyHandle(uint16_t handle, RawAddress& bd_addr) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_GetBDAddrbyHandle(handle, bd_addr);
-  }
-
-  tL2C_LCB* p_lcb = NULL;
-  bool found_dev = false;
-
-  p_lcb = l2cu_find_lcb_by_handle(handle);
-  if (p_lcb) {
-    found_dev = true;
-    bd_addr = p_lcb->remote_bd_addr;
-  }
-
-  return found_dev;
-}
-
-/*******************************************************************************
- *
- *  Function         L2CA_GetChnlFcrMode
- *
- *  Description      Get the channel FCR mode
- *
- *  Parameters:      Local CID
- *
- *  Return value:    Channel mode
- *
- ******************************************************************************/
-uint8_t L2CA_GetChnlFcrMode(uint16_t lcid) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_GetChnlFcrMode(lcid);
-  }
-
-  tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
-
-  if (p_ccb) {
-    L2CAP_TRACE_API("L2CA_GetChnlFcrMode() returns mode %d",
-                    p_ccb->peer_cfg.fcr.mode);
-    return (p_ccb->peer_cfg.fcr.mode);
-  }
-
-  L2CAP_TRACE_API("L2CA_GetChnlFcrMode() returns mode L2CAP_FCR_BASIC_MODE");
-  return (L2CAP_FCR_BASIC_MODE);
-}
-
 #if (L2CAP_NUM_FIXED_CHNLS > 0)
 /*******************************************************************************
  *
@@ -1708,10 +1459,7 @@
     }
 
     // Get a CCB and link the lcb to it
-    if (!l2cu_initialize_fixed_ccb(
-            p_lcb, fixed_cid,
-            &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL]
-                 .fixed_chnl_opts)) {
+    if (!l2cu_initialize_fixed_ccb(p_lcb, fixed_cid)) {
       L2CAP_TRACE_WARNING("%s(0x%04x) - LCB but no CCB", __func__, fixed_cid);
       return false;
     }
@@ -1738,9 +1486,7 @@
   }
 
   // Get a CCB and link the lcb to it
-  if (!l2cu_initialize_fixed_ccb(
-          p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL]
-                                 .fixed_chnl_opts)) {
+  if (!l2cu_initialize_fixed_ccb(p_lcb, fixed_cid)) {
     p_lcb->disc_reason = L2CAP_CONN_NO_RESOURCES;
     L2CAP_TRACE_WARNING("%s(0x%04x) - no CCB", __func__, fixed_cid);
     l2cu_release_lcb(p_lcb);
@@ -1836,10 +1582,7 @@
   p_buf->layer_specific = L2CAP_FLUSHABLE_CH_BASED;
 
   if (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) {
-    if (!l2cu_initialize_fixed_ccb(
-            p_lcb, fixed_cid,
-            &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL]
-                 .fixed_chnl_opts)) {
+    if (!l2cu_initialize_fixed_ccb(p_lcb, fixed_cid)) {
       L2CAP_TRACE_WARNING("L2CA_SendFixedChnlData() - no CCB for chnl: 0x%4x",
                           fixed_cid);
       osi_free(p_buf);
@@ -2000,92 +1743,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_GetCurrentConfig
- *
- * Description  This function returns configurations of L2CAP channel
- *              pp_our_cfg : pointer of our saved configuration options
- *              p_our_cfg_bits : valid config in bitmap
- *              pp_peer_cfg: pointer of peer's saved configuration options
- *              p_peer_cfg_bits : valid config in bitmap
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-bool L2CA_GetCurrentConfig(uint16_t lcid, tL2CAP_CFG_INFO** pp_our_cfg,
-                           tL2CAP_CH_CFG_BITS* p_our_cfg_bits,
-                           tL2CAP_CFG_INFO** pp_peer_cfg,
-                           tL2CAP_CH_CFG_BITS* p_peer_cfg_bits) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_GetCurrentConfig(
-        lcid, pp_our_cfg, p_our_cfg_bits, pp_peer_cfg, p_peer_cfg_bits);
-  }
-
-  tL2C_CCB* p_ccb;
-
-  L2CAP_TRACE_API("L2CA_GetCurrentConfig()  CID: 0x%04x", lcid);
-
-  p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
-
-  if (p_ccb) {
-    *pp_our_cfg = &(p_ccb->our_cfg);
-
-    /* convert valid config items into bitmap */
-    *p_our_cfg_bits = 0;
-    if (p_ccb->our_cfg.mtu_present) *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_MTU;
-    if (p_ccb->our_cfg.qos_present) *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_QOS;
-    if (p_ccb->our_cfg.flush_to_present)
-      *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO;
-    if (p_ccb->our_cfg.fcr_present) *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCR;
-    if (p_ccb->our_cfg.fcs_present) *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCS;
-    if (p_ccb->our_cfg.ext_flow_spec_present)
-      *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_EXT_FLOW_SPEC;
-
-    *pp_peer_cfg = &(p_ccb->peer_cfg);
-    *p_peer_cfg_bits = p_ccb->peer_cfg_bits;
-
-    return true;
-  } else {
-    L2CAP_TRACE_ERROR("No CCB for CID:0x%04x", lcid);
-    return false;
-  }
-}
-
-/*******************************************************************************
- *
- * Function      L2CA_GetConnectionConfig
- *
- * Description  This function returns configurations of L2CAP channel
- *              pp_l2c_ccb : pointer to this channels L2CAP ccb data.
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-bool L2CA_GetConnectionConfig(uint16_t lcid, uint16_t* mtu, uint16_t* rcid,
-                              uint16_t* handle) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    return bluetooth::shim::L2CA_GetConnectionConfig(lcid, mtu, rcid, handle);
-  }
-
-  tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
-  ;
-
-  L2CAP_TRACE_API("%s CID: 0x%04x", __func__, lcid);
-
-  if (p_ccb) {
-    *mtu = L2CAP_MTU_SIZE;
-    if (p_ccb->our_cfg.mtu_present) *mtu = p_ccb->our_cfg.mtu;
-
-    *rcid = p_ccb->remote_cid;
-    *handle = p_ccb->p_lcb->handle;
-    return true;
-  }
-
-  L2CAP_TRACE_ERROR("%s No CCB for CID:0x%04x", __func__, lcid);
-  return false;
-}
-
-/*******************************************************************************
- *
  * Function         L2CA_DataWrite
  *
  * Description      Higher layers call this function to write data.
diff --git a/stack/l2cap/l2c_ble.cc b/stack/l2cap/l2c_ble.cc
index bd1a503..c9ecb17 100644
--- a/stack/l2cap/l2c_ble.cc
+++ b/stack/l2cap/l2c_ble.cc
@@ -279,10 +279,7 @@
       LOG(ERROR) << __func__ << "failed to allocate LCB";
       return;
     } else {
-      if (!l2cu_initialize_fixed_ccb(
-              p_lcb, L2CAP_ATT_CID,
-              &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL]
-                   .fixed_chnl_opts)) {
+      if (!l2cu_initialize_fixed_ccb(p_lcb, L2CAP_ATT_CID)) {
         btm_sec_disconnect(handle, HCI_ERR_NO_CONNECTION);
         LOG(WARNING) << __func__ << "LCB but no CCB";
         return;
diff --git a/stack/l2cap/l2c_fcr.cc b/stack/l2cap/l2c_fcr.cc
index 920448b..ee48ffe 100644
--- a/stack/l2cap/l2c_fcr.cc
+++ b/stack/l2cap/l2c_fcr.cc
@@ -31,8 +31,6 @@
 
 #include "bt_common.h"
 #include "bt_types.h"
-#include "btm_api.h"
-#include "btm_int.h"
 #include "btu.h"
 #include "common/time_util.h"
 #include "hcimsgs.h"
@@ -700,8 +698,6 @@
           (ctrl_word & L2CAP_FCR_S_FRAME_BIT)) {
         if (p_ccb->fcrb.srej_sent)
           l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT);
-        else if (p_ccb->fcrb.local_busy)
-          l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT);
         else
           l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT);
 
@@ -752,7 +748,7 @@
 
   /* If we have some buffers held while doing SREJ, and SREJ has cleared,
    * process them now */
-  if ((!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.srej_sent) &&
+  if ((!p_ccb->fcrb.srej_sent) &&
       (!fixed_queue_is_empty(p_ccb->fcrb.srej_rcv_hold_q))) {
     fixed_queue_t* temp_q = p_ccb->fcrb.srej_rcv_hold_q;
     p_ccb->fcrb.srej_rcv_hold_q = fixed_queue_new(SIZE_MAX);
@@ -788,17 +784,15 @@
     fixed_queue_free(temp_q, NULL);
 
     /* Now, if needed, send one RR for the whole held queue */
-    if ((!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.rej_sent) &&
-        (!p_ccb->fcrb.srej_sent) &&
+    if ((!p_ccb->fcrb.rej_sent) && (!p_ccb->fcrb.srej_sent) &&
         (p_ccb->fcrb.next_seq_expected != p_ccb->fcrb.last_ack_sent))
       l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, 0);
     else {
       L2CAP_TRACE_DEBUG(
           "l2c_fcr_proc_pdu() not sending RR CID: 0x%04x  local_busy:%d "
           "rej_sent:%d srej_sent:%d Expected_Seq:%u Last_Ack:%u",
-          p_ccb->local_cid, p_ccb->fcrb.local_busy, p_ccb->fcrb.rej_sent,
-          p_ccb->fcrb.srej_sent, p_ccb->fcrb.next_seq_expected,
-          p_ccb->fcrb.last_ack_sent);
+          p_ccb->local_cid, 0, p_ccb->fcrb.rej_sent, p_ccb->fcrb.srej_sent,
+          p_ccb->fcrb.next_seq_expected, p_ccb->fcrb.last_ack_sent);
     }
   }
 
@@ -937,10 +931,7 @@
     l2cu_disconnect_chnl(p_ccb);
   } else {
     if (!p_ccb->fcrb.srej_sent && !p_ccb->fcrb.rej_sent) {
-      if (p_ccb->fcrb.local_busy)
-        l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_P_BIT);
-      else
-        l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT);
+      l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT);
     }
   }
 }
@@ -966,10 +957,7 @@
 #if (L2CAP_ERTM_STATS == TRUE)
     p_ccb->fcrb.xmit_ack_touts++;
 #endif
-    if (p_ccb->fcrb.local_busy)
-      l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RNR, 0);
-    else
-      l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, 0);
+    l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, 0);
   }
 }
 
@@ -1136,8 +1124,6 @@
     if (p_fcrb->send_f_rsp) {
       if (p_fcrb->srej_sent)
         l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT);
-      else if (p_fcrb->local_busy)
-        l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT);
       else
         l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT);
 
@@ -1184,15 +1170,6 @@
   /* Extract the sequence number */
   tx_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT;
 
-  /* If we have flow controlled the peer, ignore any bad I-frames from him */
-  if ((tx_seq != p_fcrb->next_seq_expected) && (p_fcrb->local_busy)) {
-    L2CAP_TRACE_WARNING("Dropping bad I-Frame since we flowed off, tx_seq:%u",
-                        tx_seq);
-    l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RNR, 0);
-    osi_free(p_buf);
-    return;
-  }
-
   /* Check if tx-sequence is the expected one */
   if (tx_seq != p_fcrb->next_seq_expected) {
     num_lost = (tx_seq - p_fcrb->next_seq_expected) & L2CAP_FCR_SEQ_MODULO;
@@ -1321,8 +1298,7 @@
   num_to_ack = (p_fcrb->next_seq_expected - p_fcrb->last_ack_sent) &
                L2CAP_FCR_SEQ_MODULO;
 
-  if ((num_to_ack < p_ccb->fcrb.max_held_acks) && (!p_fcrb->local_busy))
-    delay_ack = true;
+  if (num_to_ack < p_ccb->fcrb.max_held_acks) delay_ack = true;
 
   /* We should neve never ack frame if we are not in OPEN state */
   if ((num_to_ack != 0) && p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN)) {
@@ -1337,10 +1313,7 @@
     } else if ((fixed_queue_is_empty(p_ccb->xmit_hold_q) ||
                 l2c_fcr_is_flow_controlled(p_ccb)) &&
                fixed_queue_is_empty(p_ccb->fcrb.srej_rcv_hold_q)) {
-      if (p_fcrb->local_busy)
-        l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RNR, 0);
-      else
-        l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, 0);
+      l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, 0);
     }
   }
 }
@@ -1910,9 +1883,6 @@
   if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_ENH_RETRANS))
     p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_ERTM;
 
-  if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_STREAM_MODE))
-    p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_STREAM;
-
   /* At least one type needs to be set (Basic, ERTM, STM) to continue */
   if (!p_ccb->ertm_info.allowed_modes) {
     L2CAP_TRACE_WARNING(
@@ -1987,11 +1957,9 @@
      * Override mode from available mode options based on preference, if needed
      */
     else {
-      /* If peer does not support STREAMING, try ERTM */
-      if (p_fcr->mode == L2CAP_FCR_STREAM_MODE &&
-          !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_STREAM)) {
-        L2CAP_TRACE_DEBUG(
-            "L2C CFG: mode is STREAM, but peer does not support; Try ERTM");
+      /* There is no STREAMING use case, try ERTM */
+      if (p_fcr->mode == L2CAP_FCR_STREAM_MODE) {
+        L2CAP_TRACE_DEBUG("L2C CFG: mode is STREAM, but use case; Try ERTM");
         p_fcr->mode = L2CAP_FCR_ERTM_MODE;
       }
 
diff --git a/stack/l2cap/l2c_int.h b/stack/l2cap/l2c_int.h
index 53b6f32..cf31f47 100644
--- a/stack/l2cap/l2c_int.h
+++ b/stack/l2cap/l2c_int.h
@@ -192,7 +192,6 @@
   uint8_t max_held_acks;     /* Max acks we can hold before sending */
 
   bool remote_busy; /* true if peer has flowed us off */
-  bool local_busy;  /* true if we have flowed off the peer */
 
   bool rej_sent;       /* Reject was sent */
   bool srej_sent;      /* Selective Reject was sent */
@@ -658,8 +657,7 @@
                                                    uint16_t credit_value);
 extern void l2cu_send_peer_ble_credit_based_disconn_req(tL2C_CCB* p_ccb);
 
-extern bool l2cu_initialize_fixed_ccb(tL2C_LCB* p_lcb, uint16_t fixed_cid,
-                                      tL2CAP_FCR_OPTS* p_fcr);
+extern bool l2cu_initialize_fixed_ccb(tL2C_LCB* p_lcb, uint16_t fixed_cid);
 extern void l2cu_no_dynamic_ccbs(tL2C_LCB* p_lcb);
 extern void l2cu_process_fixed_chnl_resp(tL2C_LCB* p_lcb);
 extern bool l2cu_is_ccb_active(tL2C_CCB* p_ccb);
@@ -713,7 +711,7 @@
 extern void l2c_link_check_send_pkts(tL2C_LCB* p_lcb, tL2C_CCB* p_ccb,
                                      BT_HDR* p_buf);
 extern void l2c_link_adjust_allocation(void);
-extern void l2c_link_process_num_completed_pkts(uint8_t* p);
+extern void l2c_link_process_num_completed_pkts(uint8_t* p, uint8_t evt_len);
 extern void l2c_link_process_num_completed_blocks(uint8_t controller_id,
                                                   uint8_t* p, uint16_t evt_len);
 extern void l2c_link_processs_num_bufs(uint16_t num_lm_acl_bufs);
diff --git a/stack/l2cap/l2c_link.cc b/stack/l2cap/l2c_link.cc
index 7f6d5b9..df7edff 100644
--- a/stack/l2cap/l2c_link.cc
+++ b/stack/l2cap/l2c_link.cc
@@ -40,6 +40,7 @@
 #include "l2c_api.h"
 #include "l2c_int.h"
 #include "l2cdefs.h"
+#include "log/log.h"
 #include "osi/include/osi.h"
 
 static bool l2c_link_send_to_lower(tL2C_LCB* p_lcb, BT_HDR* p_buf,
@@ -1219,16 +1220,27 @@
  * Returns          void
  *
  ******************************************************************************/
-void l2c_link_process_num_completed_pkts(uint8_t* p) {
+void l2c_link_process_num_completed_pkts(uint8_t* p, uint8_t evt_len) {
   uint8_t num_handles, xx;
   uint16_t handle;
   uint16_t num_sent;
   tL2C_LCB* p_lcb;
 
-  STREAM_TO_UINT8(num_handles, p);
+  if (evt_len > 0) {
+    STREAM_TO_UINT8(num_handles, p);
+  } else {
+    num_handles = 0;
+  }
+
+  if (num_handles > evt_len / (2 * sizeof(uint16_t))) {
+    android_errorWriteLog(0x534e4554, "141617601");
+    num_handles = evt_len / (2 * sizeof(uint16_t));
+  }
 
   for (xx = 0; xx < num_handles; xx++) {
     STREAM_TO_UINT16(handle, p);
+    /* Extract the handle */
+    handle = HCID_GET_HANDLE(handle);
     STREAM_TO_UINT16(num_sent, p);
 
     p_lcb = l2cu_find_lcb_by_handle(handle);
diff --git a/stack/l2cap/l2c_main.cc b/stack/l2cap/l2c_main.cc
index 128f60e..5b0eaa7 100644
--- a/stack/l2cap/l2c_main.cc
+++ b/stack/l2cap/l2c_main.cc
@@ -30,7 +30,6 @@
 
 #include "bt_common.h"
 #include "bt_target.h"
-#include "btm_int.h"
 #include "btu.h"
 #include "device/include/controller.h"
 #include "hci/include/btsnoop.h"
@@ -97,6 +96,11 @@
     /* There is a slight possibility (specifically with USB) that we get an */
     /* L2CAP connection request before we get the HCI connection complete.  */
     /* So for these types of messages, hold them for up to 2 seconds.       */
+    if (l2cap_len == 0) {
+      L2CAP_TRACE_WARNING("received empty L2CAP packet");
+      osi_free(p_msg);
+      return;
+    }
     uint8_t cmd_code;
     STREAM_TO_UINT8(cmd_code, p);
 
@@ -192,10 +196,7 @@
     /* only process fixed channel data when link is open or wait for data
      * indication */
     if (!p_lcb || p_lcb->link_state == LST_DISCONNECTING ||
-        !l2cu_initialize_fixed_ccb(
-            p_lcb, rcv_cid,
-            &l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL]
-                 .fixed_chnl_opts)) {
+        !l2cu_initialize_fixed_ccb(p_lcb, rcv_cid)) {
       osi_free(p_msg);
       return;
     }
diff --git a/stack/l2cap/l2c_utils.cc b/stack/l2cap/l2c_utils.cc
index 593f12f..1081310 100644
--- a/stack/l2cap/l2c_utils.cc
+++ b/stack/l2cap/l2c_utils.cc
@@ -1902,13 +1902,13 @@
     /* Make sure service type is not a reserved value; otherwise let upper
        layer decide if acceptable
     */
-    if (p_cfg->qos.service_type <= GUARANTEED) {
+    if (p_cfg->qos.service_type <= SVC_TYPE_GUARANTEED) {
       p_ccb->peer_cfg.qos = p_cfg->qos;
       p_ccb->peer_cfg.qos_present = true;
       p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_QOS;
     } else /* Illegal service type value */
     {
-      p_cfg->qos.service_type = BEST_EFFORT;
+      p_cfg->qos.service_type = SVC_TYPE_BEST_EFFORT;
       qos_type_ok = false;
     }
   }
@@ -2016,9 +2016,6 @@
       /*                 timer value in config response shall be greater than
        * received processing time */
       p_cfg->fcr.mon_tout = p_cfg->fcr.rtrans_tout = 0;
-
-      if (p_cfg->fcr.mode == L2CAP_FCR_STREAM_MODE)
-        p_cfg->fcr.max_transmit = p_cfg->fcr.tx_win_sz = 0;
     }
 
     /* Set the threshold to send acks (may be updated in the cfg response) */
@@ -2244,7 +2241,8 @@
 
   /* Check with the BT manager if details about remote device are known */
   p_inq_info = BTM_InqDbRead(p_lcb->remote_bd_addr);
-  if (p_inq_info != NULL) {
+  if ((p_inq_info != NULL) &&
+      (p_inq_info->results.inq_result_type & BTM_INQ_RESULT_BR)) {
     page_scan_rep_mode = p_inq_info->results.page_scan_rep_mode;
     page_scan_mode = p_inq_info->results.page_scan_mode;
     clock_offset = (uint16_t)(p_inq_info->results.clock_offset);
@@ -2531,8 +2529,7 @@
  * Returns          true or false
  *
  ******************************************************************************/
-bool l2cu_initialize_fixed_ccb(tL2C_LCB* p_lcb, uint16_t fixed_cid,
-                               tL2CAP_FCR_OPTS* p_fcr) {
+bool l2cu_initialize_fixed_ccb(tL2C_LCB* p_lcb, uint16_t fixed_cid) {
 #if (L2CAP_NUM_FIXED_CHNLS > 0)
   tL2C_CCB* p_ccb;
 
@@ -2557,18 +2554,6 @@
 
   p_ccb->is_flushable = false;
 
-  if (p_fcr) {
-    /* Set the FCR parameters. For now, we will use default pools */
-    p_ccb->our_cfg.fcr = p_ccb->peer_cfg.fcr = *p_fcr;
-
-    p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE;
-    p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE;
-    p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE;
-    p_ccb->ertm_info.user_tx_buf_size = L2CAP_USER_TX_BUF_SIZE;
-
-    p_ccb->fcrb.max_held_acks = p_fcr->tx_win_sz / 3;
-  }
-
   /* Link ccb to lcb and lcb to ccb */
   p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = p_ccb;
   p_ccb->p_lcb = p_lcb;
diff --git a/stack/l2cap/l2cap_client.cc b/stack/l2cap/l2cap_client.cc
deleted file mode 100644
index b3f3737..0000000
--- a/stack/l2cap/l2cap_client.cc
+++ /dev/null
@@ -1,460 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2014 Google, Inc.
- *
- *  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.
- *
- ******************************************************************************/
-
-#define LOG_TAG "bt_l2cap_client"
-
-#include "stack/include/l2cap_client.h"
-
-#include <base/logging.h>
-#include <string.h>
-
-#include "osi/include/allocator.h"
-#include "osi/include/buffer.h"
-#include "osi/include/list.h"
-#include "osi/include/log.h"
-#include "osi/include/osi.h"
-#include "stack/include/l2c_api.h"
-
-struct l2cap_client_t {
-  l2cap_client_callbacks_t callbacks;
-  void* context;
-
-  uint16_t local_channel_id;
-  uint16_t remote_mtu;
-  bool configured_self;
-  bool configured_peer;
-  bool is_congested;
-  list_t* outbound_fragments;
-};
-
-static void connect_completed_cb(uint16_t local_channel_id,
-                                 uint16_t error_code);
-static void config_request_cb(uint16_t local_channel_id,
-                              tL2CAP_CFG_INFO* requested_parameters);
-static void config_completed_cb(uint16_t local_channel_id,
-                                tL2CAP_CFG_INFO* negotiated_parameters);
-static void disconnect_request_cb(uint16_t local_channel_id, bool ack_required);
-static void disconnect_completed_cb(uint16_t local_channel_id,
-                                    uint16_t error_code);
-static void congestion_cb(uint16_t local_channel_id, bool is_congested);
-static void read_ready_cb(uint16_t local_channel_id, BT_HDR* packet);
-static void write_completed_cb(uint16_t local_channel_id,
-                               uint16_t packets_completed);
-
-static void fragment_packet(l2cap_client_t* client, buffer_t* packet);
-static void dispatch_fragments(l2cap_client_t* client);
-static l2cap_client_t* find(uint16_t local_channel_id);
-
-// From the Bluetooth Core specification.
-static const uint16_t L2CAP_MTU_DEFAULT = 672;
-static const uint16_t L2CAP_MTU_MINIMUM = 48;
-
-static const tL2CAP_APPL_INFO l2cap_callbacks = {
-    .pL2CA_ConnectCfm_Cb = connect_completed_cb,
-    .pL2CA_ConfigInd_Cb = config_request_cb,
-    .pL2CA_ConfigCfm_Cb = config_completed_cb,
-    .pL2CA_DisconnectInd_Cb = disconnect_request_cb,
-    .pL2CA_DisconnectCfm_Cb = disconnect_completed_cb,
-    .pL2CA_DataInd_Cb = read_ready_cb,
-    .pL2CA_CongestionStatus_Cb = congestion_cb,
-    .pL2CA_TxComplete_Cb = write_completed_cb,
-};
-
-static list_t*
-    l2cap_clients;  // A list of l2cap_client_t. Container does not own objects.
-
-buffer_t* l2cap_buffer_new(size_t size) {
-  buffer_t* buf = buffer_new(size + L2CAP_MIN_OFFSET);
-  buffer_t* slice = NULL;
-  if (buf) slice = buffer_new_slice(buf, size);
-  buffer_free(buf);
-  return slice;
-}
-
-l2cap_client_t* l2cap_client_new(const l2cap_client_callbacks_t* callbacks,
-                                 void* context) {
-  CHECK(callbacks != NULL);
-  CHECK(callbacks->connected != NULL);
-  CHECK(callbacks->disconnected != NULL);
-  CHECK(callbacks->read_ready != NULL);
-  CHECK(callbacks->write_ready != NULL);
-
-  if (!l2cap_clients) {
-    l2cap_clients = list_new(NULL);
-    if (!l2cap_clients) {
-      LOG_ERROR(LOG_TAG, "%s unable to allocate space for L2CAP client list.",
-                __func__);
-      return NULL;
-    }
-  }
-
-  l2cap_client_t* ret = (l2cap_client_t*)osi_calloc(sizeof(l2cap_client_t));
-
-  ret->callbacks = *callbacks;
-  ret->context = context;
-
-  ret->remote_mtu = L2CAP_MTU_DEFAULT;
-  ret->outbound_fragments = list_new(NULL);
-
-  list_append(l2cap_clients, ret);
-
-  return ret;
-}
-
-void l2cap_client_free(l2cap_client_t* client) {
-  if (!client) return;
-
-  list_remove(l2cap_clients, client);
-  l2cap_client_disconnect(client);
-  list_free(client->outbound_fragments);
-  osi_free(client);
-}
-
-bool l2cap_client_connect(l2cap_client_t* client,
-                          const RawAddress& remote_bdaddr, uint16_t psm) {
-  CHECK(client != NULL);
-  CHECK(psm != 0);
-  CHECK(!remote_bdaddr.IsEmpty());
-  CHECK(client->local_channel_id == 0);
-  CHECK(!client->configured_self);
-  CHECK(!client->configured_peer);
-  CHECK(!L2C_INVALID_PSM(psm));
-
-  client->local_channel_id = L2CA_ConnectReq(psm, remote_bdaddr);
-  if (!client->local_channel_id) {
-    LOG_ERROR(LOG_TAG, "%s unable to create L2CAP connection.", __func__);
-    return false;
-  }
-
-  L2CA_SetConnectionCallbacks(client->local_channel_id, &l2cap_callbacks);
-  return true;
-}
-
-void l2cap_client_disconnect(l2cap_client_t* client) {
-  CHECK(client != NULL);
-
-  if (client->local_channel_id && !L2CA_DisconnectReq(client->local_channel_id))
-    LOG_ERROR(LOG_TAG, "%s unable to send disconnect message for LCID 0x%04x.",
-              __func__, client->local_channel_id);
-
-  client->local_channel_id = 0;
-  client->remote_mtu = L2CAP_MTU_DEFAULT;
-  client->configured_self = false;
-  client->configured_peer = false;
-  client->is_congested = false;
-
-  for (const list_node_t* node = list_begin(client->outbound_fragments);
-       node != list_end(client->outbound_fragments); node = list_next(node))
-    osi_free(list_node(node));
-
-  list_clear(client->outbound_fragments);
-}
-
-bool l2cap_client_is_connected(const l2cap_client_t* client) {
-  CHECK(client != NULL);
-
-  return client->local_channel_id != 0 && client->configured_self &&
-         client->configured_peer;
-}
-
-bool l2cap_client_write(l2cap_client_t* client, buffer_t* packet) {
-  CHECK(client != NULL);
-  CHECK(packet != NULL);
-  CHECK(l2cap_client_is_connected(client));
-
-  if (client->is_congested) return false;
-
-  fragment_packet(client, packet);
-  dispatch_fragments(client);
-  return true;
-}
-
-static void connect_completed_cb(uint16_t local_channel_id,
-                                 uint16_t error_code) {
-  CHECK(local_channel_id != 0);
-
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client for LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  if (error_code != L2CAP_CONN_OK) {
-    LOG_ERROR(LOG_TAG, "%s error connecting L2CAP channel: %d.", __func__,
-              error_code);
-    client->callbacks.disconnected(client, client->context);
-    return;
-  }
-
-  // Use default L2CAP parameters.
-  tL2CAP_CFG_INFO desired_parameters;
-  memset(&desired_parameters, 0, sizeof(desired_parameters));
-  if (!L2CA_ConfigReq(local_channel_id, &desired_parameters)) {
-    LOG_ERROR(LOG_TAG, "%s error sending L2CAP config parameters.", __func__);
-    client->callbacks.disconnected(client, client->context);
-  }
-}
-
-static void config_request_cb(uint16_t local_channel_id,
-                              tL2CAP_CFG_INFO* requested_parameters) {
-  tL2CAP_CFG_INFO response;
-  l2cap_client_t* client = find(local_channel_id);
-
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  memset(&response, 0, sizeof(response));
-  response.result = L2CAP_CFG_OK;
-
-  if (requested_parameters->mtu_present) {
-    // Make sure the peer chose an MTU at least as large as the minimum L2CAP
-    // MTU defined by the Bluetooth Core spec.
-    if (requested_parameters->mtu < L2CAP_MTU_MINIMUM) {
-      response.mtu = L2CAP_MTU_MINIMUM;
-      response.mtu_present = true;
-      response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
-    } else {
-      client->remote_mtu = requested_parameters->mtu;
-    }
-  }
-
-  if (requested_parameters->fcr_present) {
-    if (requested_parameters->fcr.mode != L2CAP_FCR_BASIC_MODE) {
-      response.fcr_present = true;
-      response.fcr = requested_parameters->fcr;
-      response.fcr.mode = L2CAP_FCR_BASIC_MODE;
-      response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
-    }
-  }
-
-  if (!L2CA_ConfigRsp(local_channel_id, &response)) {
-    LOG_ERROR(LOG_TAG, "%s unable to send config response for LCID 0x%04x.",
-              __func__, local_channel_id);
-    l2cap_client_disconnect(client);
-    return;
-  }
-
-  // If we've configured both endpoints, let the listener know we've connected.
-  client->configured_peer = true;
-  if (l2cap_client_is_connected(client))
-    client->callbacks.connected(client, client->context);
-}
-
-static void config_completed_cb(uint16_t local_channel_id,
-                                tL2CAP_CFG_INFO* negotiated_parameters) {
-  l2cap_client_t* client = find(local_channel_id);
-
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  switch (negotiated_parameters->result) {
-    // We'll get another configuration response later.
-    case L2CAP_CFG_PENDING:
-      break;
-
-    case L2CAP_CFG_UNACCEPTABLE_PARAMS:
-      // TODO: see if we can renegotiate parameters instead of dropping the
-      // connection.
-      LOG_WARN(
-          LOG_TAG,
-          "%s dropping L2CAP connection due to unacceptable config parameters.",
-          __func__);
-      l2cap_client_disconnect(client);
-      break;
-
-    case L2CAP_CFG_OK:
-      // If we've configured both endpoints, let the listener know we've
-      // connected.
-      client->configured_self = true;
-      if (l2cap_client_is_connected(client))
-        client->callbacks.connected(client, client->context);
-      break;
-
-    // Failure, no further parameter negotiation possible.
-    default:
-      LOG_WARN(LOG_TAG,
-               "%s L2CAP parameter negotiation failed with error code %d.",
-               __func__, negotiated_parameters->result);
-      l2cap_client_disconnect(client);
-      break;
-  }
-}
-
-static void disconnect_request_cb(uint16_t local_channel_id,
-                                  bool ack_required) {
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client with LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  if (ack_required) L2CA_DisconnectRsp(local_channel_id);
-
-  // We already sent a disconnect response so this LCID is now invalid.
-  client->local_channel_id = 0;
-  l2cap_client_disconnect(client);
-
-  client->callbacks.disconnected(client, client->context);
-}
-
-static void disconnect_completed_cb(uint16_t local_channel_id,
-                                    UNUSED_ATTR uint16_t error_code) {
-  CHECK(local_channel_id != 0);
-
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client with LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  client->local_channel_id = 0;
-  l2cap_client_disconnect(client);
-
-  client->callbacks.disconnected(client, client->context);
-}
-
-static void congestion_cb(uint16_t local_channel_id, bool is_congested) {
-  CHECK(local_channel_id != 0);
-
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  client->is_congested = is_congested;
-
-  if (!is_congested) {
-    // If we just decongested, dispatch whatever we have left over in our queue.
-    // Once that's done, if we're still decongested, notify the listener so it
-    // can start writing again.
-    dispatch_fragments(client);
-    if (!client->is_congested)
-      client->callbacks.write_ready(client, client->context);
-  }
-}
-
-static void read_ready_cb(uint16_t local_channel_id, BT_HDR* packet) {
-  CHECK(local_channel_id != 0);
-
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  // TODO(sharvil): eliminate copy from BT_HDR.
-  buffer_t* buffer = buffer_new(packet->len);
-  memcpy(buffer_ptr(buffer), packet->data + packet->offset, packet->len);
-  osi_free(packet);
-
-  client->callbacks.read_ready(client, buffer, client->context);
-  buffer_free(buffer);
-}
-
-static void write_completed_cb(UNUSED_ATTR uint16_t local_channel_id,
-                               UNUSED_ATTR uint16_t packets_completed) {
-  // Do nothing. We update congestion state based on the congestion callback
-  // and we've already removed items from outbound_fragments list so we don't
-  // really care how many packets were successfully dispatched.
-}
-
-static void fragment_packet(l2cap_client_t* client, buffer_t* packet) {
-  CHECK(client != NULL);
-  CHECK(packet != NULL);
-
-  // TODO(sharvil): eliminate copy into BT_HDR.
-  BT_HDR* bt_packet = static_cast<BT_HDR*>(
-      osi_malloc(buffer_length(packet) + L2CAP_MIN_OFFSET + sizeof(BT_HDR)));
-  bt_packet->offset = L2CAP_MIN_OFFSET;
-  bt_packet->len = buffer_length(packet);
-  memcpy(bt_packet->data + bt_packet->offset, buffer_ptr(packet),
-         buffer_length(packet));
-
-  for (;;) {
-    if (bt_packet->len <= client->remote_mtu) {
-      if (bt_packet->len > 0)
-        list_append(client->outbound_fragments, bt_packet);
-      else
-        osi_free(bt_packet);
-      break;
-    }
-
-    BT_HDR* fragment = static_cast<BT_HDR*>(
-        osi_malloc(client->remote_mtu + L2CAP_MIN_OFFSET + sizeof(BT_HDR)));
-    fragment->offset = L2CAP_MIN_OFFSET;
-    fragment->len = client->remote_mtu;
-    memcpy(fragment->data + fragment->offset,
-           bt_packet->data + bt_packet->offset, client->remote_mtu);
-
-    list_append(client->outbound_fragments, fragment);
-
-    bt_packet->offset += client->remote_mtu;
-    bt_packet->len -= client->remote_mtu;
-  }
-}
-
-static void dispatch_fragments(l2cap_client_t* client) {
-  CHECK(client != NULL);
-  CHECK(!client->is_congested);
-
-  while (!list_is_empty(client->outbound_fragments)) {
-    BT_HDR* packet = (BT_HDR*)list_front(client->outbound_fragments);
-    list_remove(client->outbound_fragments, packet);
-
-    switch (L2CA_DataWrite(client->local_channel_id, packet)) {
-      case L2CAP_DW_CONGESTED:
-        client->is_congested = true;
-        return;
-
-      case L2CAP_DW_FAILED:
-        LOG_ERROR(LOG_TAG,
-                  "%s error writing data to L2CAP connection LCID 0x%04x; "
-                  "disconnecting.",
-                  __func__, client->local_channel_id);
-        l2cap_client_disconnect(client);
-        return;
-
-      case L2CAP_DW_SUCCESS:
-        break;
-    }
-  }
-}
-
-static l2cap_client_t* find(uint16_t local_channel_id) {
-  CHECK(local_channel_id != 0);
-
-  for (const list_node_t* node = list_begin(l2cap_clients);
-       node != list_end(l2cap_clients); node = list_next(node)) {
-    l2cap_client_t* client = (l2cap_client_t*)list_node(node);
-    if (client->local_channel_id == local_channel_id) return client;
-  }
-
-  return NULL;
-}
diff --git a/stack/rfcomm/port_api.cc b/stack/rfcomm/port_api.cc
index 0bb817d..b26ff2c 100644
--- a/stack/rfcomm/port_api.cc
+++ b/stack/rfcomm/port_api.cc
@@ -31,8 +31,6 @@
 #include "osi/include/mutex.h"
 
 #include "bt_common.h"
-#include "btm_api.h"
-#include "btm_int.h"
 #include "l2c_api.h"
 #include "port_api.h"
 #include "port_int.h"
@@ -40,17 +38,8 @@
 #include "rfcdefs.h"
 #include "sdp_api.h"
 
-/* duration of break in 200ms units */
-#define PORT_BREAK_DURATION 1
-
-#define info(fmt, ...) LOG_INFO(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
-#define debug(fmt, ...) LOG_DEBUG(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
 #define error(fmt, ...) \
-  LOG_ERROR(LOG_TAG, "## ERROR : %s: " fmt "##", __func__, ##__VA_ARGS__)
-#define asrt(s)                                                            \
-  if (!(s))                                                                \
-  LOG_ERROR(LOG_TAG, "## %s assert %s failed at line:%d ##", __func__, #s, \
-            __LINE__)
+  LOG_ERROR("## ERROR : %s: " fmt "##", __func__, ##__VA_ARGS__)
 
 /* Mapping from PORT_* result codes to human readable strings. */
 static const char* result_code_strings[] = {"Success",
@@ -368,40 +357,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_SetDataCallback
- *
- * Description      This function is when a data packet is received
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_callback - address of the callback function which should
- *                               be called from the RFCOMM when data packet
- *                               is received.
- *
- *
- ******************************************************************************/
-int PORT_SetDataCallback(uint16_t port_handle, tPORT_DATA_CALLBACK* p_port_cb) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_SetDataCallback() handle:%d cb 0x%x", port_handle,
-                   p_port_cb);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[port_handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  p_port->p_data_callback = p_port_cb;
-
-  return (PORT_SUCCESS);
-}
-/*******************************************************************************
- *
  * Function         PORT_SetCODataCallback
  *
  * Description      This function is when a data packet is received
@@ -602,45 +557,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_GetRxQueueCnt
- *
- * Description      This function return number of buffers on the rx queue.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_rx_queue_count - Pointer to return queue count in.
- *
- ******************************************************************************/
-int PORT_GetRxQueueCnt(uint16_t handle, uint16_t* p_rx_queue_count) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_GetRxQueueCnt() handle:%d", handle);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (p_port->line_status) {
-    return (PORT_LINE_ERR);
-  }
-
-  *p_rx_queue_count = p_port->rx.queue_size;
-
-  RFCOMM_TRACE_API(
-      "PORT_GetRxQueueCnt() p_rx_queue_count:%d, p_port->rx.queue.count = %d",
-      *p_rx_queue_count, p_port->rx.queue_size);
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
  * Function         PORT_GetState
  *
  * Description      This function is called to fill tPORT_STATE structure
@@ -677,153 +593,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Control
- *
- * Description      This function directs a specified connection to pass control
- *                  control information to the peer device.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  signal     = specify the function to be passed
- *
- ******************************************************************************/
-int PORT_Control(uint16_t handle, uint8_t signal) {
-  tPORT* p_port;
-  uint8_t old_modem_signal;
-
-  RFCOMM_TRACE_API("PORT_Control() handle:%d signal:0x%x", handle, signal);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  old_modem_signal = p_port->local_ctrl.modem_signal;
-  p_port->local_ctrl.break_signal = 0;
-
-  switch (signal) {
-    case PORT_SET_CTSRTS:
-      p_port->local_ctrl.modem_signal |= PORT_CTSRTS_ON;
-      break;
-
-    case PORT_CLR_CTSRTS:
-      p_port->local_ctrl.modem_signal &= ~PORT_CTSRTS_ON;
-      break;
-
-    case PORT_SET_DTRDSR:
-      p_port->local_ctrl.modem_signal |= PORT_DTRDSR_ON;
-      break;
-
-    case PORT_CLR_DTRDSR:
-      p_port->local_ctrl.modem_signal &= ~PORT_DTRDSR_ON;
-      break;
-
-    case PORT_SET_RI:
-      p_port->local_ctrl.modem_signal |= PORT_RING_ON;
-      break;
-
-    case PORT_CLR_RI:
-      p_port->local_ctrl.modem_signal &= ~PORT_RING_ON;
-      break;
-
-    case PORT_SET_DCD:
-      p_port->local_ctrl.modem_signal |= PORT_DCD_ON;
-      break;
-
-    case PORT_CLR_DCD:
-      p_port->local_ctrl.modem_signal &= ~PORT_DCD_ON;
-      break;
-  }
-
-  if (signal == PORT_BREAK)
-    p_port->local_ctrl.break_signal = PORT_BREAK_DURATION;
-  else if (p_port->local_ctrl.modem_signal == old_modem_signal)
-    return (PORT_SUCCESS);
-
-  port_start_control(p_port);
-
-  RFCOMM_TRACE_EVENT(
-      "PORT_Control DTR_DSR : %d, RTS_CTS : %d, RI : %d, DCD : %d",
-      ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DTRDSR) ? 1 : 0),
-      ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RTSCTS) ? 1 : 0),
-      ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RI) ? 1 : 0),
-      ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DCD) ? 1 : 0));
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_FlowControl
- *
- * Description      This function directs a specified connection to pass
- *                  flow control message to the peer device.  Enable flag passed
- *                  shows if port can accept more data.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  enable     - enables data flow
- *
- ******************************************************************************/
-int PORT_FlowControl(uint16_t handle, bool enable) {
-  tPORT* p_port;
-  bool old_fc;
-  uint32_t events;
-
-  RFCOMM_TRACE_API("PORT_FlowControl() handle:%d enable: %d", handle, enable);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (!p_port->rfc.p_mcb) {
-    return (PORT_NOT_OPENED);
-  }
-
-  p_port->rx.user_fc = !enable;
-
-  if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) {
-    if (!p_port->rx.user_fc) {
-      port_flow_control_peer(p_port, true, 0);
-    }
-  } else {
-    old_fc = p_port->local_ctrl.fc;
-
-    /* FC is set if user is set or peer is set */
-    p_port->local_ctrl.fc = (p_port->rx.user_fc | p_port->rx.peer_fc);
-
-    if (p_port->local_ctrl.fc != old_fc) port_start_control(p_port);
-  }
-
-  /* Need to take care of the case when we could not deliver events */
-  /* to the application because we were flow controlled */
-  if (enable && (p_port->rx.queue_size != 0)) {
-    events = PORT_EV_RXCHAR;
-    if (p_port->rx_flag_ev_pending) {
-      p_port->rx_flag_ev_pending = false;
-      events |= PORT_EV_RXFLAG;
-    }
-
-    events &= p_port->ev_mask;
-    if (p_port->p_callback && events) {
-      p_port->p_callback(events, p_port->handle);
-    }
-  }
-  return (PORT_SUCCESS);
-}
-/*******************************************************************************
- *
  * Function         PORT_FlowControl_MaxCredit
  *
  * Description      This function directs a specified connection to pass
@@ -892,230 +661,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_GetModemStatus
- *
- * Description      This function retrieves modem control signals.  Normally
- *                  application will call this function after a callback
- *                  function is called with notification that one of signals
- *                  has been changed.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_signal   - specify the pointer to control signals info
- *
- ******************************************************************************/
-int PORT_GetModemStatus(uint16_t handle, uint8_t* p_signal) {
-  tPORT* p_port;
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  *p_signal = p_port->peer_ctrl.modem_signal;
-
-  RFCOMM_TRACE_API("PORT_GetModemStatus() handle:%d signal:%x", handle,
-                   *p_signal);
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_ClearError
- *
- * Description      This function retreives information about a communications
- *                  error and reports current status of a connection.  The
- *                  function should be called when an error occures to clear
- *                  the connection error flag and to enable additional read
- *                  and write operations.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_errors   - pointer of the variable to receive error codes
- *                  p_status   - pointer to the tPORT_STATUS structur to receive
- *                               connection status
- *
- ******************************************************************************/
-int PORT_ClearError(uint16_t handle, uint16_t* p_errors,
-                    tPORT_STATUS* p_status) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_ClearError() handle:%d", handle);
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  *p_errors = p_port->line_status;
-
-  /* This is the only call to clear error status.  We can not clear */
-  /* connection failed status.  To clean it port should be closed and reopened
-   */
-  p_port->line_status = (p_port->line_status & LINE_STATUS_FAILED);
-
-  PORT_GetQueueStatus(handle, p_status);
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_SendError
- *
- * Description      This function send a communications error to the peer device
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  errors     - receive error codes
- *
- ******************************************************************************/
-int PORT_SendError(uint16_t handle, uint8_t errors) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_SendError() handle:%d errors:0x%x", handle, errors);
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (!p_port->rfc.p_mcb) {
-    return (PORT_NOT_OPENED);
-  }
-
-  RFCOMM_LineStatusReq(p_port->rfc.p_mcb, p_port->dlci, errors);
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_GetQueueStatus
- *
- * Description      This function reports current status of a connection.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_status   - pointer to the tPORT_STATUS structur to receive
- *                               connection status
- *
- ******************************************************************************/
-int PORT_GetQueueStatus(uint16_t handle, tPORT_STATUS* p_status) {
-  tPORT* p_port;
-
-  /* RFCOMM_TRACE_API ("PORT_GetQueueStatus() handle:%d", handle); */
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  p_status->in_queue_size = (uint16_t)p_port->rx.queue_size;
-  p_status->out_queue_size = (uint16_t)p_port->tx.queue_size;
-
-  p_status->mtu_size = (uint16_t)p_port->peer_mtu;
-
-  p_status->flags = 0;
-
-  if (!(p_port->peer_ctrl.modem_signal & PORT_CTSRTS_ON))
-    p_status->flags |= PORT_FLAG_CTS_HOLD;
-
-  if (!(p_port->peer_ctrl.modem_signal & PORT_DTRDSR_ON))
-    p_status->flags |= PORT_FLAG_DSR_HOLD;
-
-  if (!(p_port->peer_ctrl.modem_signal & PORT_DCD_ON))
-    p_status->flags |= PORT_FLAG_RLSD_HOLD;
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_Purge
- *
- * Description      This function discards all the data from the output or
- *                  input queues of the specified connection.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  purge_flags - specify the action to take.
- *
- ******************************************************************************/
-int PORT_Purge(uint16_t handle, uint8_t purge_flags) {
-  tPORT* p_port;
-  BT_HDR* p_buf;
-  uint16_t count;
-  uint32_t events;
-
-  RFCOMM_TRACE_API("PORT_Purge() handle:%d flags:0x%x", handle, purge_flags);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (purge_flags & PORT_PURGE_RXCLEAR) {
-    mutex_global_lock(); /* to prevent missing credit */
-
-    count = fixed_queue_length(p_port->rx.queue);
-
-    while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_port->rx.queue)) != NULL)
-      osi_free(p_buf);
-
-    p_port->rx.queue_size = 0;
-
-    mutex_global_unlock();
-
-    /* If we flowed controlled peer based on rx_queue size enable data again */
-    if (count) port_flow_control_peer(p_port, true, count);
-  }
-
-  if (purge_flags & PORT_PURGE_TXCLEAR) {
-    mutex_global_lock(); /* to prevent tx.queue_size from being negative */
-
-    while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_port->tx.queue)) != NULL)
-      osi_free(p_buf);
-
-    p_port->tx.queue_size = 0;
-
-    mutex_global_unlock();
-
-    events = PORT_EV_TXEMPTY;
-
-    events |= port_flow_control_user(p_port);
-
-    events &= p_port->ev_mask;
-
-    if ((p_port->p_callback != NULL) && events)
-      (p_port->p_callback)(events, p_port->handle);
-  }
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
  * Function         PORT_ReadData
  *
  * Description      Normally not GKI aware application will call this function
@@ -1214,56 +759,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Read
- *
- * Description      Normally application will call this function after receiving
- *                  PORT_EV_RXCHAR event.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  pp_buf      - pointer to address of buffer with data,
- *
- ******************************************************************************/
-int PORT_Read(uint16_t handle, BT_HDR** pp_buf) {
-  tPORT* p_port;
-  BT_HDR* p_buf;
-
-  RFCOMM_TRACE_API("PORT_Read() handle:%d", handle);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (p_port->line_status) {
-    return (PORT_LINE_ERR);
-  }
-
-  mutex_global_lock();
-
-  p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_port->rx.queue);
-  if (p_buf) {
-    p_port->rx.queue_size -= p_buf->len;
-
-    mutex_global_unlock();
-
-    /* If rfcomm suspended traffic from the peer based on the rx_queue_size */
-    /* check if it can be resumed now */
-    port_flow_control_peer(p_port, true, 1);
-  } else {
-    mutex_global_unlock();
-  }
-
-  *pp_buf = p_buf;
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
  * Function         port_write
  *
  * Description      This function when a data packet is received from the apper
@@ -1322,64 +817,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Write
- *
- * Description      This function when a data packet is received from the apper
- *                  layer task.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  pp_buf      - pointer to address of buffer with data,
- *
- ******************************************************************************/
-int PORT_Write(uint16_t handle, BT_HDR* p_buf) {
-  tPORT* p_port;
-  uint32_t event = 0;
-  int rc;
-
-  RFCOMM_TRACE_API("PORT_Write() handle:%d", handle);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    osi_free(p_buf);
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    osi_free(p_buf);
-    return (PORT_NOT_OPENED);
-  }
-
-  if (p_port->line_status) {
-    RFCOMM_TRACE_WARNING("PORT_Write: Data dropped line_status:0x%x",
-                         p_port->line_status);
-    osi_free(p_buf);
-    return (PORT_LINE_ERR);
-  }
-
-  rc = port_write(p_port, p_buf);
-  event |= port_flow_control_user(p_port);
-
-  switch (rc) {
-    case PORT_TX_FULL:
-      event |= PORT_EV_ERR;
-      break;
-
-    case PORT_SUCCESS:
-      event |= (PORT_EV_TXCHAR | PORT_EV_TXEMPTY);
-      break;
-  }
-  /* Mask out all events that are not of interest to user */
-  event &= p_port->ev_mask;
-
-  /* Send event to the application */
-  if (p_port->p_callback && event) (p_port->p_callback)(event, p_port->handle);
-
-  return (PORT_SUCCESS);
-}
-/*******************************************************************************
- *
  * Function         PORT_WriteDataCO
  *
  * Description      Normally not GKI aware application will call this function
@@ -1647,46 +1084,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Test
- *
- * Description      Application can call this function to send RFCOMM Test frame
- *
- * Parameters:      handle      - Handle returned in the RFCOMM_CreateConnection
- *                  p_data      - Data area
- *                  max_len     - Byte count requested
- *
- ******************************************************************************/
-int PORT_Test(uint16_t handle, uint8_t* p_data, uint16_t len) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_Test() len:%d", len);
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (len > ((p_port->mtu == 0) ? RFCOMM_DEFAULT_MTU : p_port->mtu)) {
-    return (PORT_UNKNOWN_ERROR);
-  }
-
-  BT_HDR* p_buf = (BT_HDR*)osi_malloc(RFCOMM_CMD_BUF_SIZE);
-  p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2;
-  p_buf->len = len;
-
-  memcpy((uint8_t*)(p_buf + 1) + p_buf->offset, p_data, p_buf->len);
-
-  rfc_send_test(p_port->rfc.p_mcb, true, p_buf);
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
  * Function         RFCOMM_Init
  *
  * Description      This function is called to initialize RFCOMM layer
diff --git a/stack/rfcomm/port_int.h b/stack/rfcomm/port_int.h
index ecd8fbc..01eb95f 100644
--- a/stack/rfcomm/port_int.h
+++ b/stack/rfcomm/port_int.h
@@ -32,17 +32,6 @@
 #include "port_api.h"
 #include "rfcdefs.h"
 
-/* Local events passed when application event is sent from the api to PORT */
-/* ???*/
-#define PORT_EVENT_OPEN (1 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_CONTROL (2 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_SET_STATE (3 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_SET_CALLBACK (5 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_WRITE (6 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_PURGE (7 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_SEND_ERROR (8 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_FLOW_CONTROL (9 | BT_EVT_TO_BTU_SP_EVT)
-
 /*
  * Flow control configuration values for the mux
 */
@@ -114,12 +103,6 @@
  * RFCOMM Port Connection Control Block
 */
 typedef struct {
-#define RFC_PORT_STATE_IDLE 0
-#define RFC_PORT_STATE_WAIT_START 1
-#define RFC_PORT_STATE_OPENING 2
-#define RFC_PORT_STATE_OPENED 3
-#define RFC_PORT_STATE_CLOSING 4
-
   uint8_t state; /* Current state of the connection */
 
 #define RFC_RSP_PN 0x01
diff --git a/stack/rfcomm/rfc_l2cap_if.cc b/stack/rfcomm/rfc_l2cap_if.cc
index 3dc4ec2..34e0ceb 100644
--- a/stack/rfcomm/rfc_l2cap_if.cc
+++ b/stack/rfcomm/rfc_l2cap_if.cc
@@ -74,7 +74,7 @@
   p_l2c->pL2CA_CongestionStatus_Cb = RFCOMM_CongestionStatusInd;
   p_l2c->pL2CA_TxComplete_Cb = NULL;
 
-  L2CA_Register(BT_PSM_RFCOMM, p_l2c, true /* enable_snoop */);
+  L2CA_Register(BT_PSM_RFCOMM, p_l2c, true /* enable_snoop */, nullptr);
 }
 
 /*******************************************************************************
diff --git a/stack/sdp/sdp_api.cc b/stack/sdp/sdp_api.cc
index 6e5f6f1..34ac349 100644
--- a/stack/sdp/sdp_api.cc
+++ b/stack/sdp/sdp_api.cc
@@ -22,18 +22,10 @@
  *
  ******************************************************************************/
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
-#include "bt_common.h"
 #include "bt_target.h"
-#include "bt_utils.h"
-#include "hcidefs.h"
-#include "hcimsgs.h"
-#include "l2cdefs.h"
 
-#include "btu.h"
 #include "sdp_api.h"
 #include "sdpint.h"
 
@@ -217,45 +209,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_FindAttributeInDb
- *
- * Description      This function queries an SDP database for a specific
- *                  attribute. If the p_start_rec pointer is NULL, it looks from
- *                  the beginning of the database, else it continues from the
- *                  next record after p_start_rec.
- *
- * Returns          Pointer to matching record, or NULL
- *
- ******************************************************************************/
-tSDP_DISC_REC* SDP_FindAttributeInDb(tSDP_DISCOVERY_DB* p_db, uint16_t attr_id,
-                                     tSDP_DISC_REC* p_start_rec) {
-  tSDP_DISC_REC* p_rec;
-  tSDP_DISC_ATTR* p_attr;
-
-  /* Must have a valid database */
-  if (p_db == NULL) return (NULL);
-
-  if (!p_start_rec)
-    p_rec = p_db->p_first_rec;
-  else
-    p_rec = p_start_rec->p_next_rec;
-
-  while (p_rec) {
-    p_attr = p_rec->p_first_attr;
-    while (p_attr) {
-      if (p_attr->attr_id == attr_id) return (p_rec);
-
-      p_attr = p_attr->p_next_attr;
-    }
-
-    p_rec = p_rec->p_next_rec;
-  }
-  /* If here, no matching attribute found */
-  return (NULL);
-}
-
-/*******************************************************************************
- *
  * Function         SDP_FindAttributeInRec
  *
  * Description      This function searches an SDP discovery record for a
@@ -695,44 +648,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_FindAddProtoListsElemInRec
- *
- * Description      This function looks at a specific discovery record for a
- *                  protocol list element.
- *
- * Returns          true if found, false if not
- *                  If found, the passed protocol list element is filled in.
- *
- ******************************************************************************/
-bool SDP_FindAddProtoListsElemInRec(tSDP_DISC_REC* p_rec, uint16_t layer_uuid,
-                                    tSDP_PROTOCOL_ELEM* p_elem) {
-  tSDP_DISC_ATTR *p_attr, *p_sattr;
-  bool ret = false;
-
-  p_attr = p_rec->p_first_attr;
-  while (p_attr) {
-    /* Find the additional protocol descriptor list attribute */
-    if ((p_attr->attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS) &&
-        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
-      for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
-           p_sattr = p_sattr->p_next_attr) {
-        /* Safety check - each entry should itself be a sequence */
-        if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
-            DATA_ELE_SEQ_DESC_TYPE) {
-          ret = sdp_fill_proto_elem(p_sattr, layer_uuid, p_elem);
-          if (ret) break;
-        }
-      }
-      return ret;
-    }
-    p_attr = p_attr->p_next_attr;
-  }
-  /* If here, no match found */
-  return (false);
-}
-
-/*******************************************************************************
- *
  * Function         SDP_FindProfileVersionInRec
  *
  * Description      This function looks at a specific discovery record for the
diff --git a/stack/sdp/sdp_db.cc b/stack/sdp/sdp_db.cc
index ea5b84d..2cc04b0 100644
--- a/stack/sdp/sdp_db.cc
+++ b/stack/sdp/sdp_db.cc
@@ -23,17 +23,12 @@
  ******************************************************************************/
 
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 #include "bt_target.h"
 
 #include "bt_common.h"
 
-#include "hcidefs.h"
-#include "hcimsgs.h"
-#include "l2cdefs.h"
-
 #include "sdp_api.h"
 #include "sdpint.h"
 
@@ -844,67 +839,3 @@
   /* If here, not found */
   return (false);
 }
-
-/*******************************************************************************
- *
- * Function         SDP_ReadRecord
- *
- * Description      This function is called to get the raw data of the record
- *                  with the given handle from the database.
- *
- * Returns          -1, if the record is not found.
- *                  Otherwise, the offset (0 or 1) to start of data in p_data.
- *
- *                  The size of data copied into p_data is in *p_data_len.
- *
- ******************************************************************************/
-#if (SDP_RAW_DATA_INCLUDED == TRUE)
-int32_t SDP_ReadRecord(uint32_t handle, uint8_t* p_data, int32_t* p_data_len) {
-  int32_t len = 0;     /* Number of bytes in the entry */
-  int32_t offset = -1; /* default to not found */
-#if (SDP_SERVER_ENABLED == TRUE)
-  tSDP_RECORD* p_rec;
-  uint16_t start = 0;
-  uint16_t end = 0xffff;
-  tSDP_ATTRIBUTE* p_attr;
-  uint16_t rem_len;
-  uint8_t* p_rsp;
-
-  /* Find the record in the database */
-  p_rec = sdp_db_find_record(handle);
-  if (p_rec && p_data && p_data_len) {
-    p_rsp = &p_data[3];
-    while ((p_attr = sdp_db_find_attr_in_rec(p_rec, start, end)) != NULL) {
-      /* Check if attribute fits. Assume 3-byte value type/length */
-      rem_len = *p_data_len - (uint16_t)(p_rsp - p_data);
-
-      if (p_attr->len > (uint32_t)(rem_len - 6)) break;
-
-      p_rsp = sdpu_build_attrib_entry(p_rsp, p_attr);
-
-      /* next attr id */
-      start = p_attr->id + 1;
-    }
-    len = (int32_t)(p_rsp - p_data);
-
-    /* Put in the sequence header (2 or 3 bytes) */
-    if (len > 255) {
-      offset = 0;
-      p_data[0] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
-      p_data[1] = (uint8_t)((len - 3) >> 8);
-      p_data[2] = (uint8_t)(len - 3);
-    } else {
-      offset = 1;
-
-      p_data[1] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
-      p_data[2] = (uint8_t)(len - 3);
-
-      len--;
-    }
-    *p_data_len = len;
-  }
-#endif
-  /* If here, not found */
-  return (offset);
-}
-#endif
diff --git a/stack/sdp/sdp_discovery.cc b/stack/sdp/sdp_discovery.cc
index baadd7a..bdc15bd 100644
--- a/stack/sdp/sdp_discovery.cc
+++ b/stack/sdp/sdp_discovery.cc
@@ -436,6 +436,7 @@
       if (!sdp_copy_raw_data(p_ccb, false)) {
         SDP_TRACE_ERROR("sdp_copy_raw_data failed");
         sdp_disconnect(p_ccb, SDP_ILLEGAL_PARAMETER);
+        return;
       }
 
 #endif
@@ -642,6 +643,7 @@
   if (!sdp_copy_raw_data(p_ccb, true)) {
     SDP_TRACE_ERROR("sdp_copy_raw_data failed");
     sdp_disconnect(p_ccb, SDP_ILLEGAL_PARAMETER);
+    return;
   }
 #endif
 
diff --git a/stack/sdp/sdp_main.cc b/stack/sdp/sdp_main.cc
index 1198ecb..93a4f06 100644
--- a/stack/sdp/sdp_main.cc
+++ b/stack/sdp/sdp_main.cc
@@ -22,22 +22,17 @@
  *
  ******************************************************************************/
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 #include "bt_common.h"
 #include "bt_target.h"
-#include "bt_utils.h"
 #include "hcidefs.h"
-#include "hcimsgs.h"
 
 #include "l2c_api.h"
 #include "l2cdefs.h"
 #include "osi/include/osi.h"
 
 #include "btm_api.h"
-#include "btu.h"
 
 #include "sdp_api.h"
 #include "sdpint.h"
@@ -121,7 +116,8 @@
   sdp_cb.reg_info.pL2CA_TxComplete_Cb = NULL;
 
   /* Now, register with L2CAP */
-  if (!L2CA_Register(SDP_PSM, &sdp_cb.reg_info, true /* enable_snoop */)) {
+  if (!L2CA_Register(SDP_PSM, &sdp_cb.reg_info, true /* enable_snoop */,
+                     nullptr)) {
     SDP_TRACE_ERROR("SDP Registration failed");
   }
 }
@@ -133,26 +129,6 @@
   }
 }
 
-#if (SDP_DEBUG == TRUE)
-/*******************************************************************************
- *
- * Function         sdp_set_max_attr_list_size
- *
- * Description      This function sets the max attribute list size to use
- *
- * Returns          void
- *
- ******************************************************************************/
-uint16_t sdp_set_max_attr_list_size(uint16_t max_size) {
-  if (max_size > (sdp_cb.l2cap_my_cfg.mtu - 16))
-    max_size = sdp_cb.l2cap_my_cfg.mtu - 16;
-
-  sdp_cb.max_attr_list_size = max_size;
-
-  return sdp_cb.max_attr_list_size;
-}
-#endif
-
 /*******************************************************************************
  *
  * Function         sdp_connect_ind
diff --git a/stack/sdp/sdp_server.cc b/stack/sdp/sdp_server.cc
index c9949b7..9b9ad30 100644
--- a/stack/sdp/sdp_server.cc
+++ b/stack/sdp/sdp_server.cc
@@ -24,18 +24,10 @@
  ******************************************************************************/
 
 #include <log/log.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 #include "bt_common.h"
 #include "bt_types.h"
-#include "bt_utils.h"
-#include "btu.h"
-
-#include "hcidefs.h"
-#include "hcimsgs.h"
-#include "l2cdefs.h"
 
 #include "osi/include/osi.h"
 #include "sdp_api.h"
diff --git a/stack/sdp/sdp_utils.cc b/stack/sdp/sdp_utils.cc
index 33f30b0..f66a8fd 100644
--- a/stack/sdp/sdp_utils.cc
+++ b/stack/sdp/sdp_utils.cc
@@ -22,24 +22,18 @@
  *
  ******************************************************************************/
 
-#include <netinet/in.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 #include <utility>
 #include <vector>
 
 #include "bt_common.h"
 #include "bt_types.h"
-
-#include "hcidefs.h"
-#include "hcimsgs.h"
-#include "l2cdefs.h"
+#include "btif_config.h"
 
 #include "sdp_api.h"
 #include "sdpint.h"
 
-#include "btu.h"
 #include "common/metrics.h"
 
 using bluetooth::Uuid;
@@ -281,6 +275,17 @@
           bda, android::bluetooth::DeviceInfoSrcEnum::DEVICE_INFO_INTERNAL,
           ss.str(), loghex(di_record.rec.vendor), loghex(di_record.rec.product),
           loghex(di_record.rec.version), "");
+
+      std::string bda_string = bda.ToString();
+      // write manufacturer, model, HW version to config
+      btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_MANUFACTURER,
+                          di_record.rec.vendor);
+      btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_MODEL,
+                          di_record.rec.product);
+      btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_HW_VERSION,
+                          di_record.rec.version);
+      btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_VENDOR_ID_SRC,
+                          di_record.rec.vendor_id_source);
     }
   }
 }
diff --git a/stack/sdp/sdpint.h b/stack/sdp/sdpint.h
index 20c6fbd..af9ccb8 100644
--- a/stack/sdp/sdpint.h
+++ b/stack/sdp/sdpint.h
@@ -38,33 +38,6 @@
 /* Timeout definitions. */
 #define SDP_INACT_TIMEOUT_MS (30 * 1000) /* Inactivity timeout (in ms) */
 
-/* Define the Out-Flow default values. */
-#define SDP_OFLOW_QOS_FLAG 0
-#define SDP_OFLOW_SERV_TYPE 0
-#define SDP_OFLOW_TOKEN_RATE 0
-#define SDP_OFLOW_TOKEN_BUCKET_SIZE 0
-#define SDP_OFLOW_PEAK_BANDWIDTH 0
-#define SDP_OFLOW_LATENCY 0
-#define SDP_OFLOW_DELAY_VARIATION 0
-
-/* Define the In-Flow default values. */
-#define SDP_IFLOW_QOS_FLAG 0
-#define SDP_IFLOW_SERV_TYPE 0
-#define SDP_IFLOW_TOKEN_RATE 0
-#define SDP_IFLOW_TOKEN_BUCKET_SIZE 0
-#define SDP_IFLOW_PEAK_BANDWIDTH 0
-#define SDP_IFLOW_LATENCY 0
-#define SDP_IFLOW_DELAY_VARIATION 0
-
-#define SDP_LINK_TO 0
-
-/* Define the type of device notification. */
-/* (Inquiry Scan and Page Scan)            */
-#define SDP_DEVICE_NOTI_LEN \
-  (sizeof(BT_HDR) + HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1)
-
-#define SDP_DEVICE_NOTI_FLAG 0x03
-
 /* Define the Protocol Data Unit (PDU) types.
  */
 #define SDP_PDU_ERROR_RESPONSE 0x01
@@ -133,11 +106,6 @@
   tSDP_RECORD record[SDP_MAX_RECORDS];
 } tSDP_DB;
 
-enum {
-  SDP_IS_SEARCH,
-  SDP_IS_ATTR_SEARCH,
-};
-
 #if (SDP_SERVER_ENABLED == TRUE)
 /* Continuation information for the SDP server response */
 typedef struct {
@@ -226,21 +194,6 @@
 extern void sdp_free(void);
 extern void sdp_disconnect(tCONN_CB* p_ccb, uint16_t reason);
 
-#if (SDP_DEBUG == TRUE)
-extern uint16_t sdp_set_max_attr_list_size(uint16_t max_size);
-#endif
-
-/* Functions provided by sdp_conn.cc
- */
-extern void sdp_conn_rcv_l2e_conn_ind(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_conn_cfm(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_disc(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_config_ind(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_config_cfm(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_conn_failed(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_connected(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_conn_failed(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_data(BT_HDR* p_msg);
 extern void sdp_conn_timer_timeout(void* data);
 
 extern tCONN_CB* sdp_conn_originate(const RawAddress& p_bd_addr);
diff --git a/stack/smp/smp_api.cc b/stack/smp/smp_api.cc
index e082356..5b8043d 100644
--- a/stack/smp/smp_api.cc
+++ b/stack/smp/smp_api.cc
@@ -148,6 +148,7 @@
     if (!L2CA_ConnectFixedChnl(L2CAP_SMP_CID, bd_addr)) {
       tSMP_INT_DATA smp_int_data;
       smp_int_data.status = SMP_PAIR_INTERNAL_ERR;
+      p_cb->status = SMP_PAIR_INTERNAL_ERR;
       SMP_TRACE_ERROR("%s: L2C connect fixed channel failed.", __func__);
       smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data);
       return SMP_PAIR_INTERNAL_ERR;
@@ -192,6 +193,7 @@
     SMP_TRACE_ERROR("%s: L2C connect fixed channel failed.", __func__);
     tSMP_INT_DATA smp_int_data;
     smp_int_data.status = SMP_PAIR_INTERNAL_ERR;
+    p_cb->status = SMP_PAIR_INTERNAL_ERR;
     smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &smp_int_data);
     return SMP_PAIR_INTERNAL_ERR;
   }
diff --git a/stack/smp/smp_keys.cc b/stack/smp/smp_keys.cc
index 64c969d..a372ba4 100644
--- a/stack/smp/smp_keys.cc
+++ b/stack/smp/smp_keys.cc
@@ -177,7 +177,7 @@
   SMP_TRACE_DEBUG("%s", __func__);
 
   if (p_cb->le_secure_connections_mode_is_used) {
-    SMP_TRACE_WARNING("FOR LE SC LTK IS USED INSTEAD OF STK");
+    SMP_TRACE_DEBUG("FOR LE SC LTK IS USED INSTEAD OF STK");
     output = p_cb->ltk;
   } else {
     output = smp_calculate_legacy_short_term_key(p_cb);
@@ -987,7 +987,8 @@
 
   link_key_type += BTM_LTK_DERIVED_LKEY_OFFSET;
 
-  Octet16 notif_link_key = link_key;
+  Octet16 notif_link_key;
+  std::reverse_copy(link_key.begin(), link_key.end(), notif_link_key.begin());
   btm_sec_link_key_notification(bda_for_lk, notif_link_key, link_key_type);
 
   SMP_TRACE_EVENT("%s is completed", __func__);
diff --git a/stack/smp/smp_l2c.cc b/stack/smp/smp_l2c.cc
index 2daeb21..2e0025c 100644
--- a/stack/smp/smp_l2c.cc
+++ b/stack/smp/smp_l2c.cc
@@ -57,12 +57,6 @@
 void smp_l2cap_if_init(void) {
   tL2CAP_FIXED_CHNL_REG fixed_reg;
   SMP_TRACE_EVENT("SMDBG l2c %s", __func__);
-  fixed_reg.fixed_chnl_opts.mode = L2CAP_FCR_BASIC_MODE;
-  fixed_reg.fixed_chnl_opts.max_transmit = 0;
-  fixed_reg.fixed_chnl_opts.rtrans_tout = 0;
-  fixed_reg.fixed_chnl_opts.mon_tout = 0;
-  fixed_reg.fixed_chnl_opts.mps = 0;
-  fixed_reg.fixed_chnl_opts.tx_win_sz = 0;
 
   fixed_reg.pL2CA_FixedConn_Cb = smp_connect_callback;
   fixed_reg.pL2CA_FixedData_Cb = smp_data_received;
diff --git a/stack/srvc/srvc_dis.cc b/stack/srvc/srvc_dis.cc
index 3ca2e33..fd453a9 100644
--- a/stack/srvc/srvc_dis.cc
+++ b/stack/srvc/srvc_dis.cc
@@ -197,7 +197,7 @@
   srvc_eng_release_channel(conn_id);
 
   if (dis_cb.p_read_dis_cback && p_clcb) {
-    LOG_INFO(LOG_TAG, "%s conn_id:%d attr_mask = 0x%04x", __func__, conn_id,
+    LOG_INFO("%s conn_id:%d attr_mask = 0x%04x", __func__, conn_id,
              p_clcb->dis_value.attr_mask);
 
     (*dis_cb.p_read_dis_cback)(p_clcb->bda, &p_clcb->dis_value);
diff --git a/stack/test/a2dp/a2dp_vendor_ldac_decoder_test.cc b/stack/test/a2dp/a2dp_vendor_ldac_decoder_test.cc
new file mode 100644
index 0000000..ac8c260
--- /dev/null
+++ b/stack/test/a2dp/a2dp_vendor_ldac_decoder_test.cc
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <cstdint>
+
+#include "stack/include/ldacBT_bco_for_fluoride.h"
+
+#include "osi/test/AllocationTestHarness.h"
+#undef LOG_TAG
+#include "stack/a2dp/a2dp_vendor_ldac_decoder.cc"
+
+extern void allocation_tracker_uninit(void);
+namespace {
+
+uint8_t* Data(BT_HDR* packet) { return packet->data + packet->offset; }
+
+}  // namespace
+
+/**
+ * Test class to test selected functionality in stack/a2dp
+ */
+class A2dpStackTest : public AllocationTestHarness {
+ protected:
+  void SetUp() override {
+    AllocationTestHarness::SetUp();
+    // Disable our allocation tracker to allow ASAN full range
+    allocation_tracker_uninit();
+  }
+
+  void TearDown() override { AllocationTestHarness::TearDown(); }
+
+  BT_HDR* AllocateL2capPacket(const std::vector<uint8_t> data) const {
+    auto packet = AllocatePacket(data.size());
+    std::copy(data.cbegin(), data.cend(), Data(packet));
+    return packet;
+  }
+
+ private:
+  BT_HDR* AllocatePacket(size_t packet_length) const {
+    BT_HDR* packet =
+        static_cast<BT_HDR*>(osi_calloc(sizeof(BT_HDR) + packet_length));
+    packet->len = packet_length;
+    return packet;
+  }
+};
+
+TEST_F(A2dpStackTest, DecodePacket_ZeroLength) {
+  const std::vector<uint8_t> data;
+  BT_HDR* p_buf = AllocateL2capPacket(data);
+  CHECK(!a2dp_vendor_ldac_decoder_decode_packet(p_buf));
+  osi_free(p_buf);
+}
diff --git a/stack/test/a2dp/misc_fake.cc b/stack/test/a2dp/misc_fake.cc
new file mode 100644
index 0000000..6f06218
--- /dev/null
+++ b/stack/test/a2dp/misc_fake.cc
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#include "service/common/bluetooth/a2dp_codec_config.h"
+#include "stack/include/a2dp_vendor_ldac.h"
+
+bluetooth::A2dpCodecConfig* bta_av_get_a2dp_current_codec(void) {
+  return nullptr;
+}
+
+int A2DP_VendorGetTrackSampleRateLdac(const uint8_t* p_codec_info) { return 0; }
+int A2DP_VendorGetTrackBitsPerSampleLdac(const uint8_t* p_codec_info) {
+  return 0;
+}
+int A2DP_VendorGetChannelModeCodeLdac(const uint8_t* p_codec_info) { return 0; }
diff --git a/stack/test/ble_advertiser_test.cc b/stack/test/ble_advertiser_test.cc
index 952aa41..98142dd 100644
--- a/stack/test/ble_advertiser_test.cc
+++ b/stack/test/ble_advertiser_test.cc
@@ -68,6 +68,8 @@
 void alarm_free(alarm_t* alarm) {}
 const controller_t* controller_get_interface() { return nullptr; }
 
+uint64_t btm_get_next_private_addrress_interval_ms() { return 15 * 60 * 1000; }
+
 namespace {
 void DoNothing(uint8_t) {}
 
diff --git a/stack/test/common/mock_l2cap_layer.cc b/stack/test/common/mock_l2cap_layer.cc
index 4756f17..605eb49 100644
--- a/stack/test/common/mock_l2cap_layer.cc
+++ b/stack/test/common/mock_l2cap_layer.cc
@@ -25,10 +25,10 @@
 }
 
 uint16_t L2CA_Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                       bool enable_snoop) {
+                       bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info) {
   VLOG(1) << __func__ << ": psm=" << psm << ", p_cb_info=" << p_cb_info
           << ", enable_snoop=" << enable_snoop;
-  return l2cap_interface->Register(psm, p_cb_info, enable_snoop);
+  return l2cap_interface->Register(psm, p_cb_info, enable_snoop, p_ertm_info);
 }
 
 uint16_t L2CA_ConnectReq(uint16_t psm, const RawAddress& bd_addr) {
diff --git a/stack/test/common/mock_l2cap_layer.h b/stack/test/common/mock_l2cap_layer.h
index ade7585..732d415 100644
--- a/stack/test/common/mock_l2cap_layer.h
+++ b/stack/test/common/mock_l2cap_layer.h
@@ -27,7 +27,8 @@
 class L2capInterface {
  public:
   virtual uint16_t Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                            bool enable_snoop) = 0;
+                            bool enable_snoop,
+                            tL2CAP_ERTM_INFO* p_ertm_info) = 0;
   virtual uint16_t ConnectRequest(uint16_t psm, const RawAddress& bd_addr) = 0;
   virtual bool ConnectResponse(const RawAddress& bd_addr, uint8_t id,
                                uint16_t lcid, uint16_t result,
@@ -42,8 +43,9 @@
 
 class MockL2capInterface : public L2capInterface {
  public:
-  MOCK_METHOD3(Register, uint16_t(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                                  bool enable_snoop));
+  MOCK_METHOD4(Register,
+               uint16_t(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
+                        bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info));
   MOCK_METHOD2(ConnectRequest,
                uint16_t(uint16_t psm, const RawAddress& bd_addr));
   MOCK_METHOD5(ConnectResponse,
diff --git a/stack/test/rfcomm/stack_rfcomm_test.cc b/stack/test/rfcomm/stack_rfcomm_test.cc
index 52e1170..d0d1c6e 100644
--- a/stack/test/rfcomm/stack_rfcomm_test.cc
+++ b/stack/test/rfcomm/stack_rfcomm_test.cc
@@ -453,7 +453,7 @@
         &btm_security_internal_interface_);
     bluetooth::l2cap::SetMockInterface(&l2cap_interface_);
     rfcomm_callback = &rfcomm_callback_;
-    EXPECT_CALL(l2cap_interface_, Register(BT_PSM_RFCOMM, _, _))
+    EXPECT_CALL(l2cap_interface_, Register(BT_PSM_RFCOMM, _, _, _))
         .WillOnce(
             DoAll(SaveArgPointee<1>(&l2cap_appl_info_), Return(BT_PSM_RFCOMM)));
     RFCOMM_Init();
diff --git a/stack/test/stack_avrcp_test.cc b/stack/test/stack_avrcp_test.cc
new file mode 100644
index 0000000..ad1cc9e
--- /dev/null
+++ b/stack/test/stack_avrcp_test.cc
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+#include <gtest/gtest.h>
+
+#include "stack/include/avrc_api.h"
+
+class StackAvrcpTest : public ::testing::Test {
+ protected:
+  StackAvrcpTest() = default;
+
+  virtual ~StackAvrcpTest() = default;
+};
+
+TEST_F(StackAvrcpTest, test_avrcp_parse_browse_cmd) {
+  uint8_t scratch_buf[512]{};
+  tAVRC_MSG msg{};
+  tAVRC_COMMAND result{};
+  uint8_t browse_cmd_buf[512]{};
+
+  msg.hdr.opcode = AVRC_OP_BROWSE;
+  msg.browse.p_browse_data = browse_cmd_buf;
+  msg.browse.browse_len = 2;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_BAD_CMD);
+
+  memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+  browse_cmd_buf[0] = AVRC_PDU_SET_BROWSED_PLAYER;
+  msg.browse.browse_len = 3;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_BAD_CMD);
+
+  msg.browse.browse_len = 5;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_NO_ERROR);
+
+  memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+  browse_cmd_buf[0] = AVRC_PDU_GET_FOLDER_ITEMS;
+  msg.browse.browse_len = 3;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_BAD_CMD);
+
+  msg.browse.browse_len = 13;
+  uint8_t* p = &browse_cmd_buf[3];
+  UINT8_TO_STREAM(p, AVRC_SCOPE_NOW_PLAYING);  // scope
+  UINT32_TO_STREAM(p, 0x00000001);             // start_item
+  UINT32_TO_STREAM(p, 0x00000002);             // end_item
+  browse_cmd_buf[12] = 0;                      // attr_count
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_NO_ERROR);
+
+  memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+  browse_cmd_buf[0] = AVRC_PDU_CHANGE_PATH;
+  msg.browse.browse_len = 3;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_BAD_CMD);
+
+  msg.browse.browse_len = 14;
+  p = &browse_cmd_buf[3];
+  UINT16_TO_STREAM(p, 0x1234);      // uid_counter
+  UINT8_TO_STREAM(p, AVRC_DIR_UP);  // direction
+  UINT8_TO_STREAM(p, 0);            // attr_count
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_NO_ERROR);
+
+  memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+  browse_cmd_buf[0] = AVRC_PDU_GET_ITEM_ATTRIBUTES;
+  msg.browse.browse_len = 3;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_BAD_CMD);
+
+  msg.browse.browse_len = 15;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_NO_ERROR);
+
+  memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+  browse_cmd_buf[0] = AVRC_PDU_GET_TOTAL_NUM_OF_ITEMS;
+  msg.browse.browse_len = 3;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_BAD_CMD);
+
+  msg.browse.browse_len = 4;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_NO_ERROR);
+
+  memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+  browse_cmd_buf[0] = AVRC_PDU_SEARCH;
+  msg.browse.browse_len = 3;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_BAD_CMD);
+
+  p = &browse_cmd_buf[3];
+  UINT16_TO_STREAM(p, 0x0000);  // charset_id
+  UINT16_TO_STREAM(p, 0x0000);  // str_len
+  msg.browse.browse_len = 7;
+  EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+            AVRC_STS_NO_ERROR);
+}
diff --git a/test/gen_coverage.py b/test/gen_coverage.py
index c699d3b..187a90a 100755
--- a/test/gen_coverage.py
+++ b/test/gen_coverage.py
@@ -25,7 +25,6 @@
 import webbrowser
 
 from run_host_unit_tests import *
-
 """
 This script is used to generate code coverage results host supported libraries.
 The script by default will generate an html report that summarizes the coverage
@@ -63,23 +62,27 @@
         "covered_files": [
             "system/bt/profile/avrcp",
         ],
-    }, {
+    },
+    {
         "test_name": "bluetooth_test_sdp",
         "covered_files": [
             "system/bt/profile/sdp",
         ],
-    }, {
+    },
+    {
         "test_name": "test-vendor_test_host",
         "covered_files": [
             "system/bt/vendor_libs/test_vendor_lib/include",
             "system/bt/vendor_libs/test_vendor_lib/src",
         ],
-    }, {
+    },
+    {
         "test_name": "rootcanal-packets_test_host",
         "covered_files": [
             "system/bt/vendor_libs/test_vendor_lib/packets",
         ],
-    }, {
+    },
+    {
         "test_name": "bluetooth_test_common",
         "covered_files": [
             "system/bt/common",
@@ -93,320 +96,318 @@
 LLVM_MERGE = LLVM_DIR + '/llvm-profdata'
 LLVM_COV = LLVM_DIR + '/llvm-cov'
 
+
 def write_root_html_head(f):
-  # Write the header part of the root html file. This was pulled from the
-  # page source of one of the generated html files.
-  f.write("<!doctype html><html><head>" \
-    "<meta name='viewport' content='width=device-width,initial-scale=1'><met" \
-    "a charset='UTF-8'><link rel='stylesheet' type='text/css' href='style.cs" \
-    "s'></head><body><h2>Coverage Report</h2><h4>Created: " +
-    str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M')) +
-    "</h4><p>Click <a href='http://clang.llvm.org/docs/SourceBasedCodeCovera" \
-    "ge.html#interpreting-reports'>here</a> for information about interpreti" \
-    "ng this report.</p><div class='centered'><table><tr><td class='column-e" \
-    "ntry-bold'>Filename</td><td class='column-entry-bold'>Function Coverage" \
-    "</td><td class='column-entry-bold'>Instantiation Coverage</td><td class" \
-    "='column-entry-bold'>Line Coverage</td><td class='column-entry-bold'>Re" \
-    "gion Coverage</td></tr>"
-  )
+    # Write the header part of the root html file. This was pulled from the
+    # page source of one of the generated html files.
+    f.write("<!doctype html><html><head>" \
+      "<meta name='viewport' content='width=device-width,initial-scale=1'><met" \
+      "a charset='UTF-8'><link rel='stylesheet' type='text/css' href='style.cs" \
+      "s'></head><body><h2>Coverage Report</h2><h4>Created: " +
+      str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M')) +
+      "</h4><p>Click <a href='http://clang.llvm.org/docs/SourceBasedCodeCovera" \
+      "ge.html#interpreting-reports'>here</a> for information about interpreti" \
+      "ng this report.</p><div class='centered'><table><tr><td class='column-e" \
+      "ntry-bold'>Filename</td><td class='column-entry-bold'>Function Coverage" \
+      "</td><td class='column-entry-bold'>Instantiation Coverage</td><td class" \
+      "='column-entry-bold'>Line Coverage</td><td class='column-entry-bold'>Re" \
+      "gion Coverage</td></tr>"
+    )
 
 
 def write_root_html_column(f, covered, count):
-  percent = covered * 100.0 / count
-  value = "%.2f%% (%d/%d) " % (percent, covered, count)
-  color = 'column-entry-yellow'
-  if percent == 100:
-    color = 'column-entry-green'
-  if percent < 80.0:
-    color = 'column-entry-red'
-  f.write("<td class=\'" + color + "\'><pre>" + value + "</pre></td>")
+    percent = covered * 100.0 / count
+    value = "%.2f%% (%d/%d) " % (percent, covered, count)
+    color = 'column-entry-yellow'
+    if percent == 100:
+        color = 'column-entry-green'
+    if percent < 80.0:
+        color = 'column-entry-red'
+    f.write("<td class=\'" + color + "\'><pre>" + value + "</pre></td>")
 
 
 def write_root_html_rows(f, tests):
-  totals = {
-      "functions":{
-          "covered": 0,
-          "count": 0
-      },
-      "instantiations":{
-          "covered": 0,
-          "count": 0
-      },
-      "lines":{
-          "covered": 0,
-          "count": 0
-      },
-      "regions":{
-          "covered": 0,
-          "count": 0
-      }
-  }
+    totals = {
+        "functions": {
+            "covered": 0,
+            "count": 0
+        },
+        "instantiations": {
+            "covered": 0,
+            "count": 0
+        },
+        "lines": {
+            "covered": 0,
+            "count": 0
+        },
+        "regions": {
+            "covered": 0,
+            "count": 0
+        }
+    }
 
-  # Write the tests with their coverage summaries.
-  for test in tests:
-    test_name = test['test_name']
-    covered_files = test['covered_files']
-    json_results = generate_coverage_json(test)
-    test_totals = json_results['data'][0]['totals']
+    # Write the tests with their coverage summaries.
+    for test in tests:
+        test_name = test['test_name']
+        covered_files = test['covered_files']
+        json_results = generate_coverage_json(test)
+        test_totals = json_results['data'][0]['totals']
 
-    f.write("<tr class='light-row'><td><pre><a href=\'" +
-        os.path.join(test_name, "index.html") + "\'>" + test_name +
-        "</a></pre></td>")
+        f.write("<tr class='light-row'><td><pre><a href=\'" + os.path.join(test_name, "index.html") + "\'>" +
+                test_name + "</a></pre></td>")
+        for field_name in ['functions', 'instantiations', 'lines', 'regions']:
+            field = test_totals[field_name]
+            totals[field_name]['covered'] += field['covered']
+            totals[field_name]['count'] += field['count']
+            write_root_html_column(f, field['covered'], field['count'])
+        f.write("</tr>")
+
+    #Write the totals row.
+    f.write("<tr class='light-row-bold'><td><pre>Totals</a></pre></td>")
     for field_name in ['functions', 'instantiations', 'lines', 'regions']:
-      field = test_totals[field_name]
-      totals[field_name]['covered'] += field['covered']
-      totals[field_name]['count'] += field['count']
-      write_root_html_column(f, field['covered'], field['count'])
-    f.write("</tr>");
-
-  #Write the totals row.
-  f.write("<tr class='light-row-bold'><td><pre>Totals</a></pre></td>")
-  for field_name in ['functions', 'instantiations', 'lines', 'regions']:
-    field = totals[field_name]
-    write_root_html_column(f, field['covered'], field['count'])
-  f.write("</tr>");
+        field = totals[field_name]
+        write_root_html_column(f, field['covered'], field['count'])
+    f.write("</tr>")
 
 
 def write_root_html_tail(f):
-  # Pulled from the generated html coverage report.
-  f.write("</table></div><h5>Generated by llvm-cov -- llvm version 7.0.2svn<" \
-    "/h5></body></html>")
+    # Pulled from the generated html coverage report.
+    f.write("</table></div><h5>Generated by llvm-cov -- llvm version 7.0.2svn<" \
+      "/h5></body></html>")
 
 
 def generate_root_html(tests):
-  # Copy the css file from one of the coverage reports.
-  source_file = os.path.join(os.path.join(WORKING_DIR, tests[0]['test_name']), "style.css")
-  dest_file = os.path.join(WORKING_DIR, "style.css")
-  shutil.copy2(source_file, dest_file)
+    # Copy the css file from one of the coverage reports.
+    source_file = os.path.join(os.path.join(WORKING_DIR, tests[0]['test_name']), "style.css")
+    dest_file = os.path.join(WORKING_DIR, "style.css")
+    shutil.copy2(source_file, dest_file)
 
-  # Write the root index.html file that sumarizes all the tests.
-  f = open(os.path.join(WORKING_DIR, "index.html"), "w")
-  write_root_html_head(f)
-  write_root_html_rows(f, tests)
-  write_root_html_tail(f)
+    # Write the root index.html file that sumarizes all the tests.
+    f = open(os.path.join(WORKING_DIR, "index.html"), "w")
+    write_root_html_head(f)
+    write_root_html_rows(f, tests)
+    write_root_html_tail(f)
 
 
 def get_profraw_for_test(test_name):
-  test_root = get_native_test_root_or_die()
-  test_cmd = os.path.join(os.path.join(test_root, test_name), test_name)
-  if not os.path.isfile(test_cmd):
-    logging.error('The test ' + test_name + ' does not exist, please compile first')
-    sys.exit(1)
+    test_root = get_native_test_root_or_die()
+    test_cmd = os.path.join(os.path.join(test_root, test_name), test_name)
+    if not os.path.isfile(test_cmd):
+        logging.error('The test ' + test_name + ' does not exist, please compile first')
+        sys.exit(1)
 
-  profraw_file_name = test_name + ".profraw"
-  profraw_path = os.path.join(WORKING_DIR, os.path.join(test_name, profraw_file_name))
-  llvm_env_var = "LLVM_PROFILE_FILE=\"" + profraw_path + "\""
+    profraw_file_name = test_name + ".profraw"
+    profraw_path = os.path.join(WORKING_DIR, os.path.join(test_name, profraw_file_name))
+    llvm_env_var = "LLVM_PROFILE_FILE=\"" + profraw_path + "\""
 
-  test_cmd = llvm_env_var + " " + test_cmd
-  logging.info('Generating profraw data for ' + test_name)
-  logging.debug('cmd: ' + test_cmd)
-  if subprocess.call(test_cmd, shell=True) != 0:
-    logging.error('Test ' + test_name + ' failed. Please fix the test before generating coverage.')
-    sys.exit(1)
+    test_cmd = llvm_env_var + " " + test_cmd
+    logging.info('Generating profraw data for ' + test_name)
+    logging.debug('cmd: ' + test_cmd)
+    if subprocess.call(test_cmd, shell=True) != 0:
+        logging.error('Test ' + test_name + ' failed. Please fix the test before generating coverage.')
+        sys.exit(1)
 
-  if not os.path.isfile(profraw_path):
-    logging.error('Generating the profraw file failed. Did you remember to add the proper compiler flags to your build?')
-    sys.exit(1)
+    if not os.path.isfile(profraw_path):
+        logging.error(
+            'Generating the profraw file failed. Did you remember to add the proper compiler flags to your build?')
+        sys.exit(1)
 
-  return profraw_file_name
+    return profraw_file_name
 
 
 def merge_profraw_data(test_name):
-  cmd = []
-  cmd.append(os.path.join(get_android_root_or_die(), LLVM_MERGE + " merge "))
+    cmd = []
+    cmd.append(os.path.join(get_android_root_or_die(), LLVM_MERGE + " merge "))
 
-  test_working_dir = os.path.join(WORKING_DIR, test_name);
-  cmd.append(os.path.join(test_working_dir, test_name + ".profraw"))
-  profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
+    test_working_dir = os.path.join(WORKING_DIR, test_name)
+    cmd.append(os.path.join(test_working_dir, test_name + ".profraw"))
+    profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
 
-  cmd.append('-o ' + profdata_file)
-  logging.info('Combining profraw files into profdata for ' + test_name)
-  logging.debug('cmd: ' + " ".join(cmd))
-  if subprocess.call(" ".join(cmd), shell=True) != 0:
-    logging.error('Failed to merge profraw files for ' + test_name)
-    sys.exit(1)
+    cmd.append('-o ' + profdata_file)
+    logging.info('Combining profraw files into profdata for ' + test_name)
+    logging.debug('cmd: ' + " ".join(cmd))
+    if subprocess.call(" ".join(cmd), shell=True) != 0:
+        logging.error('Failed to merge profraw files for ' + test_name)
+        sys.exit(1)
 
 
 def generate_coverage_html(test):
-  COVERAGE_ROOT = '/proc/self/cwd'
+    COVERAGE_ROOT = '/proc/self/cwd'
 
-  test_name = test['test_name']
-  file_list = test['covered_files']
+    test_name = test['test_name']
+    file_list = test['covered_files']
 
-  test_working_dir = os.path.join(WORKING_DIR, test_name)
-  test_profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
+    test_working_dir = os.path.join(WORKING_DIR, test_name)
+    test_profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
 
-  cmd = [
-    os.path.join(get_android_root_or_die(), LLVM_COV),
-    "show",
-    "-format=html",
-    "-summary-only",
-    "-show-line-counts-or-regions",
-    "-show-instantiation-summary",
-    "-instr-profile=" + test_profdata_file,
-    "-path-equivalence=\"" + COVERAGE_ROOT + "\",\"" +
-        get_android_root_or_die() + "\"",
-    "-output-dir=" + test_working_dir
-  ]
+    cmd = [
+        os.path.join(get_android_root_or_die(), LLVM_COV), "show", "-format=html", "-summary-only",
+        "-show-line-counts-or-regions", "-show-instantiation-summary", "-instr-profile=" + test_profdata_file,
+        "-path-equivalence=\"" + COVERAGE_ROOT + "\",\"" + get_android_root_or_die() + "\"",
+        "-output-dir=" + test_working_dir
+    ]
 
-  # We have to have one object file not as an argument otherwise we can't specify source files.
-  test_cmd = os.path.join(os.path.join(get_native_test_root_or_die(), test_name), test_name)
-  cmd.append(test_cmd)
+    # We have to have one object file not as an argument otherwise we can't specify source files.
+    test_cmd = os.path.join(os.path.join(get_native_test_root_or_die(), test_name), test_name)
+    cmd.append(test_cmd)
 
-  # Filter out the specific files we want coverage for
-  for filename in file_list:
-    cmd.append(os.path.join(get_android_root_or_die(), filename))
+    # Filter out the specific files we want coverage for
+    for filename in file_list:
+        cmd.append(os.path.join(get_android_root_or_die(), filename))
 
-  logging.info('Generating coverage report for ' + test['test_name'])
-  logging.debug('cmd: ' + " ".join(cmd))
-  if subprocess.call(" ".join(cmd), shell=True) != 0:
-    logging.error('Failed to generate coverage for ' + test['test_name'])
-    sys.exit(1)
+    logging.info('Generating coverage report for ' + test['test_name'])
+    logging.debug('cmd: ' + " ".join(cmd))
+    if subprocess.call(" ".join(cmd), shell=True) != 0:
+        logging.error('Failed to generate coverage for ' + test['test_name'])
+        sys.exit(1)
 
 
 def generate_coverage_json(test):
-  COVERAGE_ROOT = '/proc/self/cwd'
-  test_name = test['test_name']
-  file_list = test['covered_files']
+    COVERAGE_ROOT = '/proc/self/cwd'
+    test_name = test['test_name']
+    file_list = test['covered_files']
 
-  test_working_dir = os.path.join(WORKING_DIR, test_name)
-  test_profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
+    test_working_dir = os.path.join(WORKING_DIR, test_name)
+    test_profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
 
-  cmd = [
-    os.path.join(get_android_root_or_die(), LLVM_COV),
-    "export",
-    "-summary-only",
-    "-show-region-summary",
-    "-instr-profile=" + test_profdata_file,
-    "-path-equivalence=\"" + COVERAGE_ROOT + "\",\"" + get_android_root_or_die() + "\"",
-  ]
+    cmd = [
+        os.path.join(get_android_root_or_die(), LLVM_COV),
+        "export",
+        "-summary-only",
+        "-show-region-summary",
+        "-instr-profile=" + test_profdata_file,
+        "-path-equivalence=\"" + COVERAGE_ROOT + "\",\"" + get_android_root_or_die() + "\"",
+    ]
 
-  test_cmd = os.path.join(os.path.join(get_native_test_root_or_die(), test_name), test_name)
-  cmd.append(test_cmd)
+    test_cmd = os.path.join(os.path.join(get_native_test_root_or_die(), test_name), test_name)
+    cmd.append(test_cmd)
 
-  # Filter out the specific files we want coverage for
-  for filename in file_list:
-    cmd.append(os.path.join(get_android_root_or_die(), filename))
+    # Filter out the specific files we want coverage for
+    for filename in file_list:
+        cmd.append(os.path.join(get_android_root_or_die(), filename))
 
-  logging.info('Generating coverage json for ' + test['test_name'])
-  logging.debug('cmd: ' + " ".join(cmd))
+    logging.info('Generating coverage json for ' + test['test_name'])
+    logging.debug('cmd: ' + " ".join(cmd))
 
-  json_str = subprocess.check_output(" ".join(cmd), shell=True)
-  return json.loads(json_str)
+    json_str = subprocess.check_output(" ".join(cmd), shell=True)
+    return json.loads(json_str)
 
 
 def write_json_summary(test):
-  test_name = test['test_name']
-  test_working_dir = os.path.join(WORKING_DIR, test_name)
-  test_json_summary_file = os.path.join(test_working_dir, test_name + '.json')
-  logging.debug('Writing json summary file: ' + test_json_summary_file)
-  json_file = open(test_json_summary_file, 'w')
-  json.dump(generate_coverage_json(test), json_file)
-  json_file.close()
+    test_name = test['test_name']
+    test_working_dir = os.path.join(WORKING_DIR, test_name)
+    test_json_summary_file = os.path.join(test_working_dir, test_name + '.json')
+    logging.debug('Writing json summary file: ' + test_json_summary_file)
+    json_file = open(test_json_summary_file, 'w')
+    json.dump(generate_coverage_json(test), json_file)
+    json_file.close()
 
 
 def list_tests():
-  for test in COVERAGE_TESTS:
-    print "Test Name: " + test['test_name']
-    print "Covered Files: "
-    for covered_file in test['covered_files']:
-        print "  " + covered_file
-    print
+    for test in COVERAGE_TESTS:
+        print "Test Name: " + test['test_name']
+        print "Covered Files: "
+        for covered_file in test['covered_files']:
+            print "  " + covered_file
+        print
 
 
 def main():
-  parser = argparse.ArgumentParser(description='Generate code coverage for enabled tests.')
-  parser.add_argument(
-    '-l', '--list-tests',
-    action='store_true',
-    dest='list_tests',
-    help='List all the available tests to be run as well as covered files.')
-  parser.add_argument(
-    '-a', '--all',
-    action='store_true',
-    help='Runs all available tests and prints their outputs. If no tests ' \
-         'are specified via the -t option all tests will be run.')
-  parser.add_argument(
-    '-t', '--test',
-    dest='tests',
-    action='append',
-    type=str,
-    metavar='TESTNAME',
-    default=[],
-    help='Specifies a test to be run. Multiple tests can be specified by ' \
-         'using this option multiple times. ' \
-         'Example: \"gen_coverage.py -t test1 -t test2\"')
-  parser.add_argument(
-    '-o', '--output',
-    type=str,
-    metavar='DIRECTORY',
-    default='/tmp/coverage',
-    help='Specifies the directory to store all files. The directory will be ' \
-         'created if it does not exist. Default is \"/tmp/coverage\"')
-  parser.add_argument(
-    '-s', '--skip-html',
-    dest='skip_html',
-    action='store_true',
-    help='Skip opening up the results of the coverage report in a browser.')
-  parser.add_argument(
-    '-j', '--json-file',
-    dest='json_file',
-    action='store_true',
-    help='Write out summary results to json file in test directory.')
+    parser = argparse.ArgumentParser(description='Generate code coverage for enabled tests.')
+    parser.add_argument(
+        '-l',
+        '--list-tests',
+        action='store_true',
+        dest='list_tests',
+        help='List all the available tests to be run as well as covered files.')
+    parser.add_argument(
+      '-a', '--all',
+      action='store_true',
+      help='Runs all available tests and prints their outputs. If no tests ' \
+           'are specified via the -t option all tests will be run.')
+    parser.add_argument(
+      '-t', '--test',
+      dest='tests',
+      action='append',
+      type=str,
+      metavar='TESTNAME',
+      default=[],
+      help='Specifies a test to be run. Multiple tests can be specified by ' \
+           'using this option multiple times. ' \
+           'Example: \"gen_coverage.py -t test1 -t test2\"')
+    parser.add_argument(
+      '-o', '--output',
+      type=str,
+      metavar='DIRECTORY',
+      default='/tmp/coverage',
+      help='Specifies the directory to store all files. The directory will be ' \
+           'created if it does not exist. Default is \"/tmp/coverage\"')
+    parser.add_argument(
+        '-s',
+        '--skip-html',
+        dest='skip_html',
+        action='store_true',
+        help='Skip opening up the results of the coverage report in a browser.')
+    parser.add_argument(
+        '-j',
+        '--json-file',
+        dest='json_file',
+        action='store_true',
+        help='Write out summary results to json file in test directory.')
 
-  logging.basicConfig(stream=sys.stderr, level=logging.DEBUG, format='%(levelname)s %(message)s')
-  logging.addLevelName(logging.DEBUG, "[\033[1;34m%s\033[0m]" % logging.getLevelName(logging.DEBUG))
-  logging.addLevelName(logging.INFO, "[\033[1;34m%s\033[0m]" % logging.getLevelName(logging.INFO))
-  logging.addLevelName(logging.WARNING, "[\033[1;31m%s\033[0m]" % logging.getLevelName(logging.WARNING))
-  logging.addLevelName(logging.ERROR, "[\033[1;31m%s\033[0m]" % logging.getLevelName(logging.ERROR))
+    logging.basicConfig(stream=sys.stderr, level=logging.DEBUG, format='%(levelname)s %(message)s')
+    logging.addLevelName(logging.DEBUG, "[\033[1;34m%s\033[0m]" % logging.getLevelName(logging.DEBUG))
+    logging.addLevelName(logging.INFO, "[\033[1;34m%s\033[0m]" % logging.getLevelName(logging.INFO))
+    logging.addLevelName(logging.WARNING, "[\033[1;31m%s\033[0m]" % logging.getLevelName(logging.WARNING))
+    logging.addLevelName(logging.ERROR, "[\033[1;31m%s\033[0m]" % logging.getLevelName(logging.ERROR))
 
-  args = parser.parse_args()
-  logging.debug("Args: " + str(args))
+    args = parser.parse_args()
+    logging.debug("Args: " + str(args))
 
-  # Set the working directory
-  global WORKING_DIR
-  WORKING_DIR = os.path.abspath(args.output)
-  logging.debug("Working Dir: " + WORKING_DIR)
+    # Set the working directory
+    global WORKING_DIR
+    WORKING_DIR = os.path.abspath(args.output)
+    logging.debug("Working Dir: " + WORKING_DIR)
 
-  # Print out the list of tests then exit
-  if args.list_tests:
-    list_tests()
-    sys.exit(0)
+    # Print out the list of tests then exit
+    if args.list_tests:
+        list_tests()
+        sys.exit(0)
 
-  # Check to see if a test was specified and if so only generate coverage for
-  # that test.
-  if len(args.tests) == 0:
-    args.all = True
+    # Check to see if a test was specified and if so only generate coverage for
+    # that test.
+    if len(args.tests) == 0:
+        args.all = True
 
-  tests_to_run = []
-  for test in COVERAGE_TESTS:
-    if args.all or test['test_name'] in args.tests:
-      tests_to_run.append(test)
-    if test['test_name'] in args.tests:
-      args.tests.remove(test['test_name'])
+    tests_to_run = []
+    for test in COVERAGE_TESTS:
+        if args.all or test['test_name'] in args.tests:
+            tests_to_run.append(test)
+        if test['test_name'] in args.tests:
+            args.tests.remove(test['test_name'])
 
-  # Error if a test was specified but doesn't exist.
-  if len(args.tests) != 0:
-    for test_name in args.tests:
-        logging.error('\"' + test_name + '\" was not found in the list of available tests.')
-    sys.exit(1)
+    # Error if a test was specified but doesn't exist.
+    if len(args.tests) != 0:
+        for test_name in args.tests:
+            logging.error('\"' + test_name + '\" was not found in the list of available tests.')
+        sys.exit(1)
 
-  # Generate the info for the tests
-  for test in tests_to_run:
-    logging.info('Getting coverage for ' + test['test_name'])
-    get_profraw_for_test(test['test_name'])
-    merge_profraw_data(test['test_name'])
-    if args.json_file:
-      write_json_summary(test)
-    generate_coverage_html(test)
+    # Generate the info for the tests
+    for test in tests_to_run:
+        logging.info('Getting coverage for ' + test['test_name'])
+        get_profraw_for_test(test['test_name'])
+        merge_profraw_data(test['test_name'])
+        if args.json_file:
+            write_json_summary(test)
+        generate_coverage_html(test)
 
-  # Generate the root index.html page that sumarizes all of the coverage reports.
-  generate_root_html(tests_to_run)
+    # Generate the root index.html page that sumarizes all of the coverage reports.
+    generate_root_html(tests_to_run)
 
-  # Open the results in a browser.
-  if not args.skip_html:
-    webbrowser.open('file://' + os.path.join(WORKING_DIR, 'index.html'))
+    # Open the results in a browser.
+    if not args.skip_html:
+        webbrowser.open('file://' + os.path.join(WORKING_DIR, 'index.html'))
 
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/test/headless/Android.bp b/test/headless/Android.bp
new file mode 100644
index 0000000..16b322d
--- /dev/null
+++ b/test/headless/Android.bp
@@ -0,0 +1,62 @@
+cc_test {
+    name: "bt_headless",
+    test_suites: ["device-tests"],
+    defaults: ["fluoride_defaults"],
+    srcs: [
+        "get_options.cc",
+        "headless.cc",
+        "main.cc",
+        "pairing/pairing.cc",
+        "sdp/sdp.cc",
+        "sdp/sdp_db.cc",
+        "nop/nop.cc",
+        "read/read.cc",
+        "read/name.cc",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/stack/include",
+    ],
+    whole_static_libs: [
+        "libbtcore",
+    ],
+    static_libs: [
+        "libFraunhoferAAC",
+        "libbluetooth_gd",
+        "libbt-bta",
+        "libbt-common",
+        "libbt-hci",
+        "libbt-protos-lite",
+        "libbt-sbc-decoder",
+        "libbt-sbc-encoder",
+        "libbt-stack",
+        "libbt-utils",
+        "libbtdevice",
+        "libbte",
+        "libbtif",
+        "libflatbuffers-cpp",
+        "libg722codec",
+        "libosi",
+        "libprotobuf-cpp-lite",
+        "libudrv-uipc",
+        "libz",
+    ],
+    shared_libs: [
+        "android.hardware.bluetooth.a2dp@1.0",
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
+        "libaaudio",
+        "libbase",
+        "libcrypto",
+        "libcutils",  // property_get_bool
+        "libfmq",
+        "libhidlbase",
+        "libjsoncpp",
+        "liblog",  // __android_log_print
+        "libprocessgroup",
+        "libtinyxml2",
+        "libutils",
+    ],
+    ldflags: ["-rdynamic"],
+}
diff --git a/test/headless/get_options.cc b/test/headless/get_options.cc
new file mode 100644
index 0000000..f8d8576
--- /dev/null
+++ b/test/headless/get_options.cc
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#include "test/headless/get_options.h"
+
+#include <base/logging.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <list>
+#include <string>
+
+namespace {
+constexpr struct option long_options[] = {
+    {"device", required_argument, 0, 0}, {"loop", required_argument, 0, 0},
+    {"uuid", required_argument, 0, 0},   {"msleep", required_argument, 0, 0},
+    {"stderr", no_argument, 0, 0},       {0, 0, 0, 0}};
+
+enum OptionType {
+  kOptionDevice = 0,
+  kOptionLoop = 1,
+  kOptionUuid = 2,
+  kOptionMsleep = 3,
+  kOptionStdErr = 4,
+};
+
+}  // namespace
+
+void bluetooth::test::headless::GetOpt::Usage() const {
+  fprintf(stdout, "%s: Usage:\n", name_);
+  fprintf(stdout,
+          "%s  --device=<device,>  Comma separated list of remote devices\n",
+          name_);
+  fprintf(stdout, "%s  --uuid=<uuid,>      Comma separated list of uuids\n",
+          name_);
+  fprintf(stdout, "%s  --loop=<loop>       Number of loops\n", name_);
+  fprintf(stdout, "%s  --msleep=<msecs>    Sleep msec between loops\n", name_);
+  fprintf(stdout, "%s  --stderr            Dump stderr to stdout\n", name_);
+  fflush(nullptr);
+}
+
+void bluetooth::test::headless::GetOpt::ParseValue(
+    char* optarg, std::list<std::string>& string_list) {
+  CHECK(optarg != nullptr);
+  char* p = optarg;
+  char* pp = optarg;
+  while (*p != '\0') {
+    if (*p == ',') {
+      *p = 0;
+      string_list.push_back(std::string(pp));
+      pp = p + 1;
+    }
+    p++;
+  }
+  if (pp != p) string_list.push_back(std::string(pp));
+}
+
+void bluetooth::test::headless::GetOpt::ProcessOption(int option_index,
+                                                      char* optarg) {
+  std::list<std::string> string_list;
+  OptionType option_type = static_cast<OptionType>(option_index);
+
+  switch (option_type) {
+    case kOptionDevice:
+      if (!optarg) return;
+      ParseValue(optarg, string_list);
+      for (auto& entry : string_list) {
+        if (RawAddress::IsValidAddress(entry)) {
+          RawAddress address;
+          RawAddress::FromString(entry, address);
+          device_.push_back(address);
+        }
+      }
+      break;
+    case kOptionLoop:
+      loop_ = std::stoul(optarg, nullptr, 0);
+      break;
+    case kOptionUuid:
+      if (!optarg) return;
+      ParseValue(optarg, string_list);
+      for (auto& entry : string_list) {
+        uuid_.push_back(
+            bluetooth::Uuid::From16Bit(std::stoul(entry.c_str(), nullptr, 0)));
+      }
+      break;
+    case kOptionMsleep:
+      if (!optarg) return;
+      msec_ = std::stoul(optarg, nullptr, 0);
+      break;
+    case kOptionStdErr:
+      close_stderr_ = false;
+      break;
+    default:
+      fflush(nullptr);
+      valid_ = false;
+      return;
+      break;
+  }
+}
+
+bluetooth::test::headless::GetOpt::GetOpt(int argc, char** argv)
+    : name_(argv[0]) {
+  while (1) {
+    int option_index = 0;
+    int c = getopt_long_only(argc, argv, "d:l:u:", long_options, &option_index);
+    if (c == -1) break;
+
+    switch (c) {
+      case 0:
+        ProcessOption(static_cast<OptionType>(option_index), optarg);
+        break;
+      case '?':
+        Usage();
+        valid_ = false;
+        return;
+      default:
+        printf("?? getopt returned character code 0%o ??\n", c);
+    }
+  }
+
+  while (optind < argc) {
+    non_options_.push_back(argv[optind++]);
+  }
+  fflush(nullptr);
+}
+
diff --git a/test/headless/get_options.h b/test/headless/get_options.h
new file mode 100644
index 0000000..04a5025
--- /dev/null
+++ b/test/headless/get_options.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <list>
+#include "types/bluetooth/uuid.h"
+#include "types/raw_address.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+class GetOpt {
+ public:
+  GetOpt(int argc, char** arv);
+  virtual ~GetOpt() = default;
+
+  virtual void Usage() const;
+  virtual bool IsValid() const { return valid_; };
+
+  std::string GetNextSubTest() const {
+    std::string test = non_options_.front();
+    non_options_.pop_front();
+    return test;
+  }
+
+  std::list<RawAddress> device_;
+  std::list<bluetooth::Uuid> uuid_;
+  unsigned long loop_{1};
+  unsigned long msec_{0};
+
+  bool close_stderr_{true};
+
+  mutable std::list<std::string> non_options_;
+
+ private:
+  void ParseValue(char* optarg, std::list<std::string>& my_list);
+  void ProcessOption(int option_index, char* optarg);
+  const char* name_{nullptr};
+  bool valid_{true};
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/headless.cc b/test/headless/headless.cc
new file mode 100644
index 0000000..872a5bd
--- /dev/null
+++ b/test/headless/headless.cc
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_headless"
+
+#include <dlfcn.h>  //  dlopen
+
+#include "base/logging.h"  // LOG() stdout and android log
+#include "include/hardware/bluetooth.h"
+#include "osi/include/log.h"  // android log only
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
+extern bt_interface_t bluetoothInterface;
+
+using namespace bluetooth::test::headless;
+
+namespace {
+std::mutex adapter_state_mutex_;
+std::condition_variable adapter_state_cv_;
+bt_state_t bt_state_{BT_STATE_OFF};
+
+void adapter_state_changed(bt_state_t state) {
+  std::unique_lock<std::mutex> lck(adapter_state_mutex_);
+  bt_state_ = state;
+  adapter_state_cv_.notify_all();
+}
+void adapter_properties(bt_status_t status, int num_properties,
+                        bt_property_t* properties) {
+  LOG_INFO("%s", __func__);
+}
+
+void remote_device_properties(bt_status_t status, RawAddress* bd_addr,
+                              int num_properties, bt_property_t* properties) {
+  LOG_INFO("%s", __func__);
+}
+
+void device_found(int num_properties, bt_property_t* properties) {
+  LOG_INFO("%s", __func__);
+}
+
+void discovery_state_changed(bt_discovery_state_t state) {
+  LOG_INFO("%s", __func__);
+}
+
+/** Bluetooth Legacy PinKey Request callback */
+void pin_request(RawAddress* remote_bd_addr, bt_bdname_t* bd_name, uint32_t cod,
+                 bool min_16_digit) {
+  LOG_INFO("%s", __func__);
+}
+
+void ssp_request(RawAddress* remote_bd_addr, bt_bdname_t* bd_name, uint32_t cod,
+                 bt_ssp_variant_t pairing_variant, uint32_t pass_key) {
+  LOG_INFO("%s", __func__);
+}
+
+/** Bluetooth Bond state changed callback */
+/* Invoked in response to create_bond, cancel_bond or remove_bond */
+void bond_state_changed(bt_status_t status, RawAddress* remote_bd_addr,
+                        bt_bond_state_t state) {
+  LOG_INFO("%s", __func__);
+}
+
+/** Bluetooth ACL connection state changed callback */
+void acl_state_changed(bt_status_t status, RawAddress* remote_bd_addr,
+                       bt_acl_state_t state) {
+  LOG_INFO("%s", __func__);
+}
+
+void thread_event(bt_cb_thread_evt evt) { LOG_INFO("%s", __func__); }
+
+void dut_mode_recv(uint16_t opcode, uint8_t* buf, uint8_t len) {
+  LOG_INFO("%s", __func__);
+}
+
+void le_test_mode(bt_status_t status, uint16_t num_packets) {
+  LOG_INFO("%s", __func__);
+}
+
+void energy_info(bt_activity_energy_info* energy_info,
+                 bt_uid_traffic_t* uid_data) {
+  LOG_INFO("%s", __func__);
+}
+
+bt_callbacks_t bt_callbacks{
+    /** set to sizeof(bt_callbacks_t) */
+    .size = sizeof(bt_callbacks_t),
+    .adapter_state_changed_cb = adapter_state_changed,
+    .adapter_properties_cb = adapter_properties,
+    .remote_device_properties_cb = remote_device_properties,
+    .device_found_cb = device_found,
+    .discovery_state_changed_cb = discovery_state_changed,
+    .pin_request_cb = pin_request,
+    .ssp_request_cb = ssp_request,
+    .bond_state_changed_cb = bond_state_changed,
+    .acl_state_changed_cb = acl_state_changed,
+    .thread_evt_cb = thread_event,
+    .dut_mode_recv_cb = dut_mode_recv,
+    .le_test_mode_cb = le_test_mode,
+    .energy_info_cb = energy_info,
+};
+// HAL HARDWARE CALLBACKS
+
+// OS CALLOUTS
+bool set_wake_alarm_co(uint64_t delay_millis, bool should_wake, alarm_cb cb,
+                       void* data) {
+  LOG_INFO("%s", __func__);
+  return true;
+}
+int acquire_wake_lock_co(const char* lock_name) {
+  LOG_INFO("%s", __func__);
+  return 1;
+}
+
+int release_wake_lock_co(const char* lock_name) {
+  LOG_INFO("%s", __func__);
+  return 0;
+}
+
+bt_os_callouts_t bt_os_callouts{
+    .size = sizeof(bt_os_callouts_t),
+    .set_wake_alarm = set_wake_alarm_co,
+    .acquire_wake_lock = acquire_wake_lock_co,
+    .release_wake_lock = release_wake_lock_co,
+};
+}  // namespace
+
+void HeadlessStack::SetUp() {
+  LOG(INFO) << __func__ << " Entry";
+
+  int status = bluetoothInterface.init(&bt_callbacks, false, false, 0, nullptr);
+  (status == BT_STATUS_SUCCESS)
+      ? LOG(INFO) << __func__ << " Initialized bluetooth callbacks"
+      : LOG(FATAL) << "Failed to initialize Bluetooth stack";
+
+  status = bluetoothInterface.set_os_callouts(&bt_os_callouts);
+  (status == BT_STATUS_SUCCESS)
+      ? LOG(INFO) << __func__ << " Initialized os callouts"
+      : LOG(ERROR) << "Failed to set up Bluetooth OS callouts";
+
+  bluetoothInterface.enable();
+  LOG_INFO("%s HeadlessStack stack has enabled", __func__);
+
+  std::unique_lock<std::mutex> lck(adapter_state_mutex_);
+  while (bt_state_ != BT_STATE_ON) adapter_state_cv_.wait(lck);
+  LOG_INFO("%s HeadlessStack stack is operational", __func__);
+}
+
+void HeadlessStack::TearDown() {
+  LOG_INFO("Stack has disabled");
+  int status = bluetoothInterface.disable();
+
+  LOG(INFO) << __func__ << " Interface has been disabled status:" << status;
+
+  bluetoothInterface.cleanup();
+  LOG(INFO) << __func__ << " Cleaned up hal bluetooth library";
+
+  std::unique_lock<std::mutex> lck(adapter_state_mutex_);
+  while (bt_state_ != BT_STATE_OFF) adapter_state_cv_.wait(lck);
+  LOG_INFO("%s HeadlessStack stack has exited", __func__);
+}
diff --git a/test/headless/headless.h b/test/headless/headless.h
new file mode 100644
index 0000000..551f60f
--- /dev/null
+++ b/test/headless/headless.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include <unistd.h>
+
+#include "base/logging.h"  // LOG() stdout and android log
+#include "test/headless/get_options.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+namespace {
+
+template <typename T>
+using ExecutionUnit = std::function<T()>;
+
+constexpr char kHeadlessStartSentinel[] =
+    " START HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS "
+    "HEADLESS";
+constexpr char kHeadlessStopSentinel[] =
+    " STOP HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS "
+    "HEADLESS";
+
+}  // namespace
+
+class HeadlessStack {
+ protected:
+  HeadlessStack() = default;
+  virtual ~HeadlessStack() = default;
+
+  void SetUp();
+  void TearDown();
+};
+
+class HeadlessRun : public HeadlessStack {
+ protected:
+  const bluetooth::test::headless::GetOpt& options_;
+  unsigned long loop_{0};
+
+  HeadlessRun(const bluetooth::test::headless::GetOpt& options)
+      : options_(options) {}
+
+  template <typename T>
+  T RunOnHeadlessStack(ExecutionUnit<T> func) {
+    SetUp();
+    LOG(INFO) << kHeadlessStartSentinel;
+
+    T rc;
+    for (loop_ = 0; loop_ < options_.loop_; loop_++) {
+      rc = func();
+      if (options_.msec_ != 0) {
+        usleep(options_.msec_ * 1000);
+      }
+      if (rc) {
+        break;
+      }
+    }
+    if (rc) {
+      LOG(ERROR) << "FAIL:" << rc << " loop/loops:" << loop_ << "/"
+                 << options_.loop_;
+    } else {
+      LOG(INFO) << "PASS:" << rc << " loop/loops:" << loop_ << "/"
+                << options_.loop_;
+    }
+
+    LOG(INFO) << kHeadlessStopSentinel;
+    TearDown();
+    return rc;
+  }
+  virtual ~HeadlessRun() = default;
+};
+
+template <typename T>
+class HeadlessTest : public HeadlessRun {
+ public:
+  virtual T Run() {
+    if (options_.non_options_.size() == 0) {
+      fprintf(stdout, "Must supply at least one subtest name\n");
+      return -1;
+    }
+
+    std::string subtest = options_.GetNextSubTest();
+    if (test_nodes_.find(subtest) == test_nodes_.end()) {
+      fprintf(stdout, "Unknown subtest module:%s\n", subtest.c_str());
+      return -1;
+    }
+    return test_nodes_.at(subtest)->Run();
+  }
+
+  virtual ~HeadlessTest() = default;
+
+ protected:
+  HeadlessTest(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessRun(options) {}
+
+  std::unordered_map<std::string, std::unique_ptr<HeadlessTest<T>>> test_nodes_;
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/main.cc b/test/headless/main.cc
new file mode 100644
index 0000000..487e96c
--- /dev/null
+++ b/test/headless/main.cc
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_headless"
+
+#include <unordered_map>
+
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/nop/nop.h"
+#include "test/headless/pairing/pairing.h"
+#include "test/headless/read/read.h"
+#include "test/headless/sdp/sdp.h"
+
+using namespace bluetooth::test::headless;
+
+namespace {
+
+class Main : public HeadlessTest<int> {
+ public:
+  Main(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {
+    test_nodes_.emplace(
+        "nop", std::make_unique<bluetooth::test::headless::Nop>(options));
+    test_nodes_.emplace(
+        "pairing",
+        std::make_unique<bluetooth::test::headless::Pairing>(options));
+    test_nodes_.emplace(
+        "read", std::make_unique<bluetooth::test::headless::Read>(options));
+    test_nodes_.emplace(
+        "sdp", std::make_unique<bluetooth::test::headless::Sdp>(options));
+  }
+
+  int Run() override {
+    if (options_.close_stderr_) {
+      fclose(stderr);
+    }
+    return HeadlessTest<int>::Run();
+  }
+};
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  fflush(nullptr);
+  setvbuf(stdout, nullptr, _IOLBF, 0);
+
+  bluetooth::test::headless::GetOpt options(argc, argv);
+  if (!options.IsValid()) {
+    return -1;
+  }
+
+  Main main(options);
+  return main.Run();
+}
diff --git a/test/headless/nop/nop.cc b/test/headless/nop/nop.cc
new file mode 100644
index 0000000..f6ac42c
--- /dev/null
+++ b/test/headless/nop/nop.cc
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_headless_sdp"
+
+#include <future>
+
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "stack/include/sdp_api.h"
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/nop/nop.h"
+#include "types/raw_address.h"
+
+int bluetooth::test::headless::Nop::Run() {
+  return RunOnHeadlessStack<int>([this]() {
+    fprintf(stdout, "Nop loop:%lu\n", loop_);
+    return 0;
+  });
+}
diff --git a/gd/shim/iconnectability.h b/test/headless/nop/nop.h
similarity index 62%
copy from gd/shim/iconnectability.h
copy to test/headless/nop/nop.h
index fbfaaa3..e65efa3 100644
--- a/gd/shim/iconnectability.h
+++ b/test/headless/nop/nop.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,21 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
-/**
- * The gd API exported to the legacy api
- */
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
 namespace bluetooth {
-namespace shim {
+namespace test {
+namespace headless {
 
-struct IConnectability {
-  virtual void StartConnectability() = 0;
-  virtual void StopConnectability() = 0;
-  virtual bool IsConnectable() const = 0;
-
-  virtual ~IConnectability() {}
+class Nop : public HeadlessTest<int> {
+ public:
+  Nop(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {}
+  int Run() override;
 };
 
-}  // namespace shim
+}  // namespace headless
+}  // namespace test
 }  // namespace bluetooth
diff --git a/test/headless/pairing/pairing.cc b/test/headless/pairing/pairing.cc
new file mode 100644
index 0000000..7785ea4
--- /dev/null
+++ b/test/headless/pairing/pairing.cc
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_headless_sdp"
+
+#include <future>
+
+#include "base/logging.h"  // LOG() stdout and android log
+#include "btif/include/btif_api.h"
+#include "osi/include/log.h"  // android log only
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/pairing/pairing.h"
+#include "types/raw_address.h"
+
+int bluetooth::test::headless::Pairing::Run() {
+  if (options_.loop_ < 1) {
+    fprintf(stdout, "This test requires at least a single loop");
+    options_.Usage();
+    return -1;
+  }
+  if (options_.device_.size() != 1) {
+    fprintf(stdout, "This test requires a single device specified");
+    options_.Usage();
+    return -1;
+  }
+
+  RawAddress raw_address = options_.device_.front();
+
+  return RunOnHeadlessStack<int>([raw_address]() {
+    bt_status_t status = btif_dm_create_bond(&raw_address, BT_TRANSPORT_BR_EDR);
+    switch (status) {
+      case BT_STATUS_SUCCESS:
+        break;
+      default:
+        fprintf(stdout, "Failed to create bond status:%d", status);
+        break;
+    }
+    return status;
+  });
+}
diff --git a/test/headless/pairing/pairing.h b/test/headless/pairing/pairing.h
new file mode 100644
index 0000000..4125d48
--- /dev/null
+++ b/test/headless/pairing/pairing.h
@@ -0,0 +1,20 @@
+
+#pragma once
+
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+class Pairing : public HeadlessTest<int> {
+ public:
+  Pairing(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {}
+  int Run() override;
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/read/name.cc b/test/headless/read/name.cc
new file mode 100644
index 0000000..6a8ea9f
--- /dev/null
+++ b/test/headless/read/name.cc
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_headless_sdp"
+
+#include <future>
+
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "stack/include/btm_api.h"
+#include "stack/include/btm_api_types.h"
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/read/name.h"
+#include "types/raw_address.h"
+
+std::promise<tBTM_REMOTE_DEV_NAME> promise_;
+
+void RemoteNameCallback(void* data) {
+  promise_.set_value(*static_cast<tBTM_REMOTE_DEV_NAME*>(data));
+}
+
+int bluetooth::test::headless::Name::Run() {
+  if (options_.loop_ < 1) {
+    fprintf(stdout, "This test requires at least a single loop");
+    options_.Usage();
+    return -1;
+  }
+  if (options_.device_.size() != 1) {
+    fprintf(stdout, "This test requires a single device specified");
+    options_.Usage();
+    return -1;
+  }
+
+  const RawAddress& raw_address = options_.device_.front();
+
+  return RunOnHeadlessStack<int>([&raw_address]() {
+    promise_ = std::promise<tBTM_REMOTE_DEV_NAME>();
+
+    auto future = promise_.get_future();
+
+    tBTM_STATUS status = BTM_ReadRemoteDeviceName(
+        raw_address, &RemoteNameCallback, BT_TRANSPORT_BR_EDR);
+    if (status != BTM_CMD_STARTED) {
+      fprintf(stdout, "Failure to start read remote device\n");
+      return -1;
+    }
+
+    tBTM_REMOTE_DEV_NAME name_packet = future.get();
+    switch (name_packet.status) {
+      case BTM_SUCCESS: {
+        char buf[BD_NAME_LEN];
+        memcpy(buf, name_packet.remote_bd_name, BD_NAME_LEN);
+        std::string name(buf);
+        fprintf(stdout, "Name result mac:%s name:%s\n",
+                raw_address.ToString().c_str(), name.c_str());
+      } break;
+      case BTM_BAD_VALUE_RET:
+        fprintf(stdout, "Name Timeout or other failure");
+        return -2;
+      default:
+        fprintf(stdout, "Unexpected remote name request failure status:%hd",
+                name_packet.status);
+        return -2;
+    }
+    return 0;
+  });
+}
diff --git a/gd/shim/iconnectability.h b/test/headless/read/name.h
similarity index 62%
copy from gd/shim/iconnectability.h
copy to test/headless/read/name.h
index fbfaaa3..587cee2 100644
--- a/gd/shim/iconnectability.h
+++ b/test/headless/read/name.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,21 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
-/**
- * The gd API exported to the legacy api
- */
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
 namespace bluetooth {
-namespace shim {
+namespace test {
+namespace headless {
 
-struct IConnectability {
-  virtual void StartConnectability() = 0;
-  virtual void StopConnectability() = 0;
-  virtual bool IsConnectable() const = 0;
-
-  virtual ~IConnectability() {}
+class Name : public HeadlessTest<int> {
+ public:
+  Name(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {}
+  int Run() override;
 };
 
-}  // namespace shim
+}  // namespace headless
+}  // namespace test
 }  // namespace bluetooth
diff --git a/test/headless/read/read.cc b/test/headless/read/read.cc
new file mode 100644
index 0000000..d1a3b8d
--- /dev/null
+++ b/test/headless/read/read.cc
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_headless"
+
+#include "test/headless/read/read.h"
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/read/name.h"
+
+using namespace bluetooth::test::headless;
+
+Read::Read(const bluetooth::test::headless::GetOpt& options)
+    : HeadlessTest<int>(options) {
+  test_nodes_.emplace(
+      "name", std::make_unique<bluetooth::test::headless::Name>(options));
+}
diff --git a/gd/l2cap/security_policy.h b/test/headless/read/read.h
similarity index 65%
copy from gd/l2cap/security_policy.h
copy to test/headless/read/read.h
index 5a06401..04ab0f2 100644
--- a/gd/l2cap/security_policy.h
+++ b/test/headless/read/read.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,12 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
 namespace bluetooth {
-namespace l2cap {
+namespace test {
+namespace headless {
 
-class SecurityPolicy {};
+class Read : public HeadlessTest<int> {
+ public:
+  Read(const bluetooth::test::headless::GetOpt& options);
+};
 
-}  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/sdp/sdp.cc b/test/headless/sdp/sdp.cc
new file mode 100644
index 0000000..9e20340
--- /dev/null
+++ b/test/headless/sdp/sdp.cc
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_headless_sdp"
+
+#include <future>
+
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "stack/include/sdp_api.h"
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/sdp/sdp.h"
+#include "test/headless/sdp/sdp_db.h"
+#include "types/raw_address.h"
+
+using namespace bluetooth::test::headless;
+
+static void bta_jv_start_discovery_callback(uint16_t result, void* user_data) {
+  auto promise = static_cast<std::promise<uint16_t>*>(user_data);
+  promise->set_value(result);
+}
+
+namespace {
+
+struct sdp_error_code_s {
+  const char* name;
+  uint16_t error_code;
+} sdp_error_code[] = {
+    {"KsdpSuccess", 0},
+    {"KsdpInvalidVersion", 0x0001},
+    {"KsdpInvalidServRecHdl", 0x0002},
+    {"KsdpInvalidReqSyntax", 0x0003},
+    {"KsdpInvalidPduSize", 0x0004},
+    {"KsdpInvalidContState", 0x0005},
+    {"KsdpNoResources", 0x0006},
+    {"KsdpDiRegFailed", 0x0007},
+    {"KsdpDiDiscFailed", 0x0008},
+    {"KsdpNoDiRecordFound", 0x0009},
+    {"KsdpErrAttrNotPresent", 0x000a},
+    {"KsdpIllegalParameter", 0x000b},
+    {"KsdpNoRecsMatch", 0xFFF0},
+    {"KsdpConnFailed", 0xFFF1},
+    {"KsdpCfgFailed", 0xFFF2},
+    {"KsdpGenericError", 0xFFF3},
+    {"KsdpDbFull", 0xFFF4},
+    {"KsdpInvalidPdu", 0xFFF5},
+    {"KsdpSecurityErr", 0xFFF6},
+    {"KsdpConnRejected", 0xFFF7},
+    {"KsdpCancel", 0xFFF8},
+};
+
+const char* kUnknownText = "Unknown";
+
+const char* SdpErrorCodeToString(uint16_t code) {
+  for (size_t i = 0; i < sizeof(sdp_error_code) / sizeof(sdp_error_code_s);
+       ++i) {
+    if (sdp_error_code[i].error_code == code) {
+      return sdp_error_code[i].name;
+    }
+  }
+  return kUnknownText;
+}
+
+constexpr size_t kMaxDiscoveryRecords = 64;
+
+int sdp_query_uuid(unsigned int num_loops, const RawAddress& raw_address,
+                   const bluetooth::Uuid& uuid) {
+  SdpDb sdp_discovery_db(kMaxDiscoveryRecords);
+
+  if (!SDP_InitDiscoveryDb(sdp_discovery_db.RawPointer(),
+                           sdp_discovery_db.Length(),
+                           1,  // num_uuid,
+                           &uuid, 0, nullptr)) {
+    fprintf(stdout, "%s Unable to initialize sdp discovery\n", __func__);
+    return -1;
+  }
+
+  std::promise<uint16_t> promise;
+  auto future = promise.get_future();
+
+  sdp_discovery_db.Print(stdout);
+
+  if (!SDP_ServiceSearchAttributeRequest2(
+          raw_address, sdp_discovery_db.RawPointer(),
+          bta_jv_start_discovery_callback, (void*)&promise)) {
+    fprintf(stdout, "%s Failed to start search attribute request\n", __func__);
+    return -2;
+  }
+
+  uint16_t result = future.get();
+  if (result != 0) {
+    fprintf(stdout, "Failed search discovery result:%s\n",
+            SdpErrorCodeToString(result));
+    return result;
+  }
+
+  tSDP_DISC_REC* rec = SDP_FindServiceInDb(sdp_discovery_db.RawPointer(),
+                                           uuid.As16Bit(), nullptr);
+  if (rec == nullptr) {
+    fprintf(stdout, "discovery record is null from:%s uuid:%s\n",
+            raw_address.ToString().c_str(), uuid.ToString().c_str());
+  } else {
+    fprintf(stdout, "result:%d attr_id:%x from:%s uuid:%s\n", result,
+            rec->p_first_attr->attr_id, rec->remote_bd_addr.ToString().c_str(),
+            uuid.ToString().c_str());
+  }
+  return 0;
+}
+
+}  // namespace
+
+int bluetooth::test::headless::Sdp::Run() {
+  if (options_.loop_ < 1) {
+    printf("This test requires at least a single loop\n");
+    options_.Usage();
+    return -1;
+  }
+  if (options_.device_.size() != 1) {
+    printf("This test requires a single device specified\n");
+    options_.Usage();
+    return -1;
+  }
+  if (options_.uuid_.size() != 1) {
+    printf("This test requires a single uuid specified\n");
+    options_.Usage();
+    return -1;
+  }
+
+  return RunOnHeadlessStack<int>([this]() {
+    return sdp_query_uuid(options_.loop_, options_.device_.front(),
+                          options_.uuid_.front());
+  });
+}
diff --git a/gd/shim/iconnectability.h b/test/headless/sdp/sdp.h
similarity index 62%
copy from gd/shim/iconnectability.h
copy to test/headless/sdp/sdp.h
index fbfaaa3..6542a1d 100644
--- a/gd/shim/iconnectability.h
+++ b/test/headless/sdp/sdp.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,21 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
-/**
- * The gd API exported to the legacy api
- */
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
 namespace bluetooth {
-namespace shim {
+namespace test {
+namespace headless {
 
-struct IConnectability {
-  virtual void StartConnectability() = 0;
-  virtual void StopConnectability() = 0;
-  virtual bool IsConnectable() const = 0;
-
-  virtual ~IConnectability() {}
+class Sdp : public HeadlessTest<int> {
+ public:
+  Sdp(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {}
+  int Run() override;
 };
 
-}  // namespace shim
+}  // namespace headless
+}  // namespace test
 }  // namespace bluetooth
diff --git a/test/headless/sdp/sdp_db.cc b/test/headless/sdp/sdp_db.cc
new file mode 100644
index 0000000..023861b
--- /dev/null
+++ b/test/headless/sdp/sdp_db.cc
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "bt_headless"
+
+#include "test/headless/sdp/sdp_db.h"
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "stack/include/sdp_api.h"
+#include "types/bluetooth/uuid.h"
+#include "types/raw_address.h"
+
+using namespace bluetooth::test::headless;
+
+SdpDb::SdpDb(unsigned int max_records) : max_records_(max_records) {
+  db_ = (tSDP_DISCOVERY_DB*)malloc(max_records_ * sizeof(tSDP_DISC_REC) +
+                                   sizeof(tSDP_DISCOVERY_DB));
+}
+
+SdpDb::~SdpDb() { free(db_); }
+
+tSDP_DISCOVERY_DB* SdpDb::RawPointer() { return db_; }
+
+uint32_t SdpDb::Length() const {
+  return max_records_ * sizeof(tSDP_DISC_REC) + sizeof(tSDP_DISCOVERY_DB);
+}
+
+void SdpDb::Print(FILE* filep) const {
+  fprintf(filep, "memory size:0x%x free:0x%x\n", db_->mem_size, db_->mem_free);
+  fprintf(filep, "number of filters:%hd\n", db_->num_uuid_filters);
+  for (int i = 0; i < db_->num_uuid_filters; i++) {
+    fprintf(filep, "  uuid:%s\n", db_->uuid_filters[i].ToString().c_str());
+  }
+  fprintf(filep, "raw data size:0x%x used:0x%x\n", db_->raw_size,
+          db_->raw_used);
+}
diff --git a/gd/cert/grpc_root_server.h b/test/headless/sdp/sdp_db.h
similarity index 60%
rename from gd/cert/grpc_root_server.h
rename to test/headless/sdp/sdp_db.h
index 2ebfe79..63de0cc 100644
--- a/gd/cert/grpc_root_server.h
+++ b/test/headless/sdp/sdp_db.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -16,26 +16,31 @@
 
 #pragma once
 
-#include <memory>
-#include <string>
+#include <cstdint>
+#include <cstdio>
 
-#include <grpc++/grpc++.h>
+#include "stack/include/sdp_api.h"
 
 namespace bluetooth {
-namespace cert {
+namespace test {
+namespace headless {
 
-class GrpcRootServer {
+class SdpDb {
  public:
-  void StartServer(const std::string& address, int grpc_root_server_port, int grpc_port);
+  SdpDb(unsigned int max_records);
+  ~SdpDb();
 
-  void StopServer();
+  tSDP_DISCOVERY_DB* RawPointer();
 
-  void RunGrpcLoop();
+  uint32_t Length() const;
+
+  void Print(FILE* filep) const;
 
  private:
-  bool started_ = false;
-  std::unique_ptr<::grpc::Server> server_ = nullptr;
+  unsigned int max_records_;
+  tSDP_DISCOVERY_DB* db_;
 };
 
-}  // namespace cert
+}  // namespace headless
+}  // namespace test
 }  // namespace bluetooth
diff --git a/test/rootcanal/Android.bp b/test/rootcanal/Android.bp
index bd62e50..1094afc 100644
--- a/test/rootcanal/Android.bp
+++ b/test/rootcanal/Android.bp
@@ -15,6 +15,7 @@
 
 cc_binary {
     name: "android.hardware.bluetooth@1.1-service.sim",
+    defaults: ["gd_defaults"],
     proprietary: true,
     relative_install_path: "hw",
     srcs: [
@@ -32,6 +33,7 @@
         "libhidlbase",
         "liblog",
         "libutils",
+        "libprotobuf-cpp-lite",
     ],
     cflags: [
         "-fvisibility=hidden",
@@ -49,6 +51,7 @@
         "android.hardware.bluetooth-hci",
         "libbt-rootcanal",
         "libbt-rootcanal-types",
+        "libscriptedbeaconpayload-protos-lite",
     ],
     include_dirs: [
         "system/bt",
@@ -62,6 +65,7 @@
 
 cc_library_shared {
     name: "android.hardware.bluetooth@1.1-impl-sim",
+    defaults: ["gd_defaults"],
     proprietary: true,
     relative_install_path: "hw",
     srcs: [
@@ -78,6 +82,7 @@
         "libhidlbase",
         "liblog",
         "libutils",
+        "libprotobuf-cpp-lite",
     ],
     cflags: [
         "-Wall",
@@ -94,6 +99,7 @@
         "android.hardware.bluetooth-hci",
         "libbt-rootcanal",
         "libbt-rootcanal-types",
+        "libscriptedbeaconpayload-protos-lite",
     ],
     include_dirs: [
         "system/bt",
diff --git a/test/rootcanal/bluetooth_hci.cc b/test/rootcanal/bluetooth_hci.cc
index 2bebd9a..de95989 100644
--- a/test/rootcanal/bluetooth_hci.cc
+++ b/test/rootcanal/bluetooth_hci.cc
@@ -18,13 +18,13 @@
 
 #include "bluetooth_hci.h"
 
+#include "log/log.h"
 #include <cutils/properties.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <string.h>
 
 #include "hci_internals.h"
-#include "os/log.h"
 
 namespace android {
 namespace hardware {
@@ -41,7 +41,7 @@
 
 bool BtTestConsoleEnabled() {
   // Assume enabled by default.
-  return property_get_bool("bt.rootcanal_test_console", true);
+  return property_get_bool("vendor.bt.rootcanal_test_console", true);
 }
 
 }  // namespace
@@ -85,7 +85,6 @@
     const sp<V1_0::IBluetoothHciCallbacks>& cb,
     const sp<V1_1::IBluetoothHciCallbacks>& cb_1_1) {
   LOG_INFO("%s", __func__);
-
   if (cb == nullptr) {
     LOG_ERROR("cb == nullptr! -> Unable to call initializationComplete(ERR)");
     return Void();
@@ -105,7 +104,7 @@
   controller_ = std::make_shared<DualModeController>();
 
   char mac_property[PROPERTY_VALUE_MAX] = "";
-  property_get("bt.rootcanal_mac_address", mac_property, "3C:5A:B4:01:02:03");
+  property_get("vendor.bt.rootcanal_mac_address", mac_property, "3C:5A:B4:01:02:03");
   controller_->Initialize({"dmc", std::string(mac_property)});
 
   controller_->RegisterEventChannel(
@@ -165,8 +164,17 @@
       [this](AsyncTaskId task) { async_manager_.CancelAsyncTask(task); });
 
   test_model_.Reset();
+
   // Add the controller as a device in the model.
-  test_model_.Add(controller_);
+  size_t controller_index = test_model_.Add(controller_);
+  size_t low_energy_phy_index =
+      test_model_.AddPhy(test_vendor_lib::Phy::Type::LOW_ENERGY);
+  size_t classic_phy_index =
+      test_model_.AddPhy(test_vendor_lib::Phy::Type::BR_EDR);
+  test_model_.AddDeviceToPhy(controller_index, low_energy_phy_index);
+  test_model_.AddDeviceToPhy(controller_index, classic_phy_index);
+  test_model_.SetTimerPeriod(std::chrono::milliseconds(10));
+  test_model_.StartTimer();
 
   // Send responses to logcat if the test channel is not configured.
   test_channel_.RegisterSendResponse([](const std::string& response) {
@@ -179,18 +187,21 @@
                    [this](int fd) { test_model_.IncomingHciConnection(fd); });
     SetUpLinkLayerServer(
         6311, [this](int fd) { test_model_.IncomingLinkLayerConnection(fd); });
+  } else {
+    // This should be configurable in the future.
+    LOG_INFO("Adding Beacons so the scan list is not empty.");
+    test_channel_.Add({"beacon", "be:ac:10:00:00:01", "1000"});
+    test_model_.AddDeviceToPhy(controller_index + 1, low_energy_phy_index);
+    test_channel_.Add({"beacon", "be:ac:10:00:00:02", "1000"});
+    test_model_.AddDeviceToPhy(controller_index + 2, low_energy_phy_index);
+    test_channel_.Add(
+        {"scripted_beacon", "5b:ea:c1:00:00:03",
+         "/data/vendor/bluetooth/bluetooth_sim_ble_playback_file",
+         "/data/vendor/bluetooth/bluetooth_sim_ble_playback_events"});
+    test_model_.AddDeviceToPhy(controller_index + 3, low_energy_phy_index);
+    test_channel_.List({});
   }
 
-  // Add some default devices for easier debugging
-  test_channel_.AddDefaults();
-
-  // This should be configurable in the future.
-  LOG_INFO("Adding Beacons so the scan list is not empty.");
-  test_channel_.Add({"beacon", "be:ac:10:00:00:01", "1000"});
-  test_channel_.AddDeviceToPhy({"2", "1"});
-  test_channel_.Add({"beacon", "be:ac:10:00:00:02", "1000"});
-  test_channel_.AddDeviceToPhy({"3", "1"});
-
   unlink_cb_ = [this, cb](sp<BluetoothDeathRecipient>& death_recipient) {
     if (death_recipient->getHasDied())
       LOG_INFO("Skipping unlink call, service died.");
diff --git a/test/run_host_unit_tests.py b/test/run_host_unit_tests.py
index 3d3f1c7..992ca31 100755
--- a/test/run_host_unit_tests.py
+++ b/test/run_host_unit_tests.py
@@ -21,201 +21,191 @@
 # Registered host based unit tests
 # Must have 'host_supported: true'
 HOST_TESTS = [
-  'bluetooth_test_common',
-  'bluetoothtbd_test',
-  'net_test_avrcp',
-  'net_test_btcore',
-  'net_test_types',
-  'net_test_btpackets',
+    'bluetooth_test_common',
+    'bluetoothtbd_test',
+    'net_test_avrcp',
+    'net_test_btcore',
+    'net_test_types',
+    'net_test_btpackets',
 ]
 
 SOONG_UI_BASH = 'build/soong/soong_ui.bash'
 
+
 def str2bool(argument, default=False):
-  """ Convert a string to a booleen value. """
-  argument = str(argument)
-  if argument.lower() in ['0', 'f', 'false', 'off', 'no', 'n']:
-    return False
-  elif argument.lower() in ['1', 't', 'true', 'on', 'yes', 'y']:
-    return True
-  return default
+    """ Convert a string to a booleen value. """
+    argument = str(argument)
+    if argument.lower() in ['0', 'f', 'false', 'off', 'no', 'n']:
+        return False
+    elif argument.lower() in ['1', 't', 'true', 'on', 'yes', 'y']:
+        return True
+    return default
 
 
 def check_dir_exists(dir, dirname):
-  if not os.path.isdir(dir):
-    print "Couldn't find %s (%s)!" % (dirname, dir)
-    sys.exit(0)
+    if not os.path.isdir(dir):
+        print "Couldn't find %s (%s)!" % (dirname, dir)
+        sys.exit(0)
 
 
 def get_output_from_command(cmd):
-  try:
-    return subprocess.check_output(cmd).strip()
-  except subprocess.CalledProcessError as e:
-    print 'Failed to call {cmd}, return code {code}'.format(cmd=cmd,
-                                                            code=e.returncode)
-    print e
-    return None
+    try:
+        return subprocess.check_output(cmd).strip()
+    except subprocess.CalledProcessError as e:
+        print 'Failed to call {cmd}, return code {code}'.format(cmd=cmd, code=e.returncode)
+        print e
+        return None
 
 
 def get_android_root_or_die():
-  value = os.environ.get('ANDROID_BUILD_TOP')
-  if not value:
-    # Try to find build/soong/soong_ui.bash upwards until root directory
-    current_path = os.path.abspath(os.getcwd())
-    while current_path and os.path.isdir(current_path):
-      soong_ui_bash_path = os.path.join(current_path, SOONG_UI_BASH)
-      if os.path.isfile(soong_ui_bash_path):
-        # Use value returned from Soong UI instead in case definition to TOP
-        # changes in the future
-        value = get_output_from_command((soong_ui_bash_path,
-                                         '--dumpvar-mode',
-                                         '--abs',
-                                         'TOP'))
-        break
-      parent_path = os.path.abspath(os.path.join(current_path, os.pardir))
-      if parent_path == current_path:
-        current_path = None
-      else:
-        current_path = parent_path
+    value = os.environ.get('ANDROID_BUILD_TOP')
     if not value:
-      print 'Cannot determine ANDROID_BUILD_TOP'
-      sys.exit(1)
-  check_dir_exists(value, '$ANDROID_BUILD_TOP')
-  return value
+        # Try to find build/soong/soong_ui.bash upwards until root directory
+        current_path = os.path.abspath(os.getcwd())
+        while current_path and os.path.isdir(current_path):
+            soong_ui_bash_path = os.path.join(current_path, SOONG_UI_BASH)
+            if os.path.isfile(soong_ui_bash_path):
+                # Use value returned from Soong UI instead in case definition to TOP
+                # changes in the future
+                value = get_output_from_command((soong_ui_bash_path, '--dumpvar-mode', '--abs', 'TOP'))
+                break
+            parent_path = os.path.abspath(os.path.join(current_path, os.pardir))
+            if parent_path == current_path:
+                current_path = None
+            else:
+                current_path = parent_path
+        if not value:
+            print 'Cannot determine ANDROID_BUILD_TOP'
+            sys.exit(1)
+    check_dir_exists(value, '$ANDROID_BUILD_TOP')
+    return value
 
 
 def get_android_host_out_or_die():
-  value = os.environ.get('ANDROID_HOST_OUT')
-  if not value:
-    ANDROID_BUILD_TOP = get_android_root_or_die()
-    value = get_output_from_command((os.path.join(ANDROID_BUILD_TOP,
-                                                  SOONG_UI_BASH),
-                                     '--dumpvar-mode',
-                                     '--abs',
-                                     'HOST_OUT'))
+    value = os.environ.get('ANDROID_HOST_OUT')
     if not value:
-      print 'Cannot determine ANDROID_HOST_OUT'
-      sys.exit(1)
-  check_dir_exists(value, '$ANDROID_HOST_OUT')
-  return value
+        ANDROID_BUILD_TOP = get_android_root_or_die()
+        value = get_output_from_command((os.path.join(ANDROID_BUILD_TOP, SOONG_UI_BASH), '--dumpvar-mode', '--abs',
+                                         'HOST_OUT'))
+        if not value:
+            print 'Cannot determine ANDROID_HOST_OUT'
+            sys.exit(1)
+    check_dir_exists(value, '$ANDROID_HOST_OUT')
+    return value
 
 
 def get_android_dist_dir_or_die():
-  # Check if $DIST_DIR is predefined as environment variable
-  value = os.environ.get('DIST_DIR')
-  if not value:
-    # If not use the default path
-    ANDROID_BUILD_TOP = get_android_root_or_die()
-    value = os.path.join(os.path.join(ANDROID_BUILD_TOP, 'out'), 'dist')
-  if not os.path.isdir(value):
-    if os.path.exists(value):
-      print '%s is not a directory!' % (value)
-      sys.exit(1)
-    os.makedirs(value)
-  return value
+    # Check if $DIST_DIR is predefined as environment variable
+    value = os.environ.get('DIST_DIR')
+    if not value:
+        # If not use the default path
+        ANDROID_BUILD_TOP = get_android_root_or_die()
+        value = os.path.join(os.path.join(ANDROID_BUILD_TOP, 'out'), 'dist')
+    if not os.path.isdir(value):
+        if os.path.exists(value):
+            print '%s is not a directory!' % (value)
+            sys.exit(1)
+        os.makedirs(value)
+    return value
 
 
 def get_native_test_root_or_die():
-  android_host_out = get_android_host_out_or_die()
-  test_root = os.path.join(android_host_out, 'nativetest64')
-  if not os.path.isdir(test_root):
-    test_root = os.path.join(android_host_out, 'nativetest')
+    android_host_out = get_android_host_out_or_die()
+    test_root = os.path.join(android_host_out, 'nativetest64')
     if not os.path.isdir(test_root):
-      print 'Neither nativetest64 nor nativetest directory exist,' \
-            ' please compile first'
-      sys.exit(1)
-  return test_root
+        test_root = os.path.join(android_host_out, 'nativetest')
+        if not os.path.isdir(test_root):
+            print 'Neither nativetest64 nor nativetest directory exist,' \
+                  ' please compile first'
+            sys.exit(1)
+    return test_root
 
 
 def get_test_cmd_or_die(test_root, test_name, enable_xml, test_filter):
-  test_path = os.path.join(os.path.join(test_root, test_name), test_name)
-  if not os.path.isfile(test_path):
-    print 'Cannot find: ' + test_path
-    sys.exit(1)
-  cmd = [test_path]
-  if enable_xml:
-    dist_dir = get_android_dist_dir_or_die()
-    log_output_path = os.path.join(dist_dir, 'gtest/{0}_test_details.xml'
-                                   .format(test_name))
-    cmd.append('--gtest_output=xml:{0}'.format(log_output_path))
-  if test_filter:
-    cmd.append('--gtest_filter=%s' % test_filter)
-  return cmd
+    test_path = os.path.join(os.path.join(test_root, test_name), test_name)
+    if not os.path.isfile(test_path):
+        print 'Cannot find: ' + test_path
+        sys.exit(1)
+    cmd = [test_path]
+    if enable_xml:
+        dist_dir = get_android_dist_dir_or_die()
+        log_output_path = os.path.join(dist_dir, 'gtest/{0}_test_details.xml'.format(test_name))
+        cmd.append('--gtest_output=xml:{0}'.format(log_output_path))
+    if test_filter:
+        cmd.append('--gtest_filter=%s' % test_filter)
+    return cmd
 
 
 # path is relative to Android build top
 def build_target(target, num_tasks):
-  ANDROID_BUILD_TOP = get_android_root_or_die()
-  build_cmd = [SOONG_UI_BASH, '--make-mode']
-  if num_tasks > 1:
-    build_cmd.append('-j' + str(num_tasks))
-  build_cmd.append(target)
-  p = subprocess.Popen(build_cmd, cwd=ANDROID_BUILD_TOP, env=os.environ.copy())
-  return_code = p.wait()
-  if return_code != 0:
-    print 'BUILD FAILED, return code: {0}'.format(str(return_code))
-    sys.exit(1)
-  return
+    ANDROID_BUILD_TOP = get_android_root_or_die()
+    build_cmd = [SOONG_UI_BASH, '--make-mode']
+    if num_tasks > 1:
+        build_cmd.append('-j' + str(num_tasks))
+    build_cmd.append(target)
+    p = subprocess.Popen(build_cmd, cwd=ANDROID_BUILD_TOP, env=os.environ.copy())
+    return_code = p.wait()
+    if return_code != 0:
+        print 'BUILD FAILED, return code: {0}'.format(str(return_code))
+        sys.exit(1)
+    return
 
 
 def main():
-  """ run_host_unit_tests.py - Run registered host based unit tests
+    """ run_host_unit_tests.py - Run registered host based unit tests
   """
-  parser = argparse.ArgumentParser(description='Run host based unit tests.')
-  parser.add_argument(
-      '--enable_xml',
-      type=str2bool,
-      dest='enable_xml',
-      nargs='?',
-      const=True,
-      default=False,
-      help=
-      'Whether to output structured XML log output in out/dist/gtest directory')
-  parser.add_argument(
-      '-j',
-      type=int,
-      nargs='?',
-      dest='num_tasks',
-      const=-1,
-      default=-1,
-      help='Number of tasks to run at the same time')
-  parser.add_argument(
-      'rest',
-      nargs=argparse.REMAINDER,
-      help='-- args, other gtest arguments for each individual test')
-  args = parser.parse_args()
+    parser = argparse.ArgumentParser(description='Run host based unit tests.')
+    parser.add_argument(
+        '--enable_xml',
+        type=str2bool,
+        dest='enable_xml',
+        nargs='?',
+        const=True,
+        default=False,
+        help='Whether to output structured XML log output in out/dist/gtest directory')
+    parser.add_argument(
+        '-j',
+        type=int,
+        nargs='?',
+        dest='num_tasks',
+        const=-1,
+        default=-1,
+        help='Number of tasks to run at the same time')
+    parser.add_argument(
+        'rest', nargs=argparse.REMAINDER, help='-- args, other gtest arguments for each individual test')
+    args = parser.parse_args()
 
-  build_target('MODULES-IN-system-bt', args.num_tasks)
-  TEST_ROOT = get_native_test_root_or_die()
-  test_results = []
-  for test in HOST_TESTS:
-    test_cmd = get_test_cmd_or_die(TEST_ROOT, test, args.enable_xml, args.rest)
-    if subprocess.call(test_cmd) != 0:
-      test_results.append(False)
-    else:
-      test_results.append(True)
-  if not all(test_results):
-    failures = [i for i, x in enumerate(test_results) if not x]
-    for index in failures:
-      print 'TEST FAILLED: ' + HOST_TESTS[index]
+    build_target('MODULES-IN-system-bt', args.num_tasks)
+    TEST_ROOT = get_native_test_root_or_die()
+    test_results = []
+    for test in HOST_TESTS:
+        test_cmd = get_test_cmd_or_die(TEST_ROOT, test, args.enable_xml, args.rest)
+        if subprocess.call(test_cmd) != 0:
+            test_results.append(False)
+        else:
+            test_results.append(True)
+    if not all(test_results):
+        failures = [i for i, x in enumerate(test_results) if not x]
+        for index in failures:
+            print 'TEST FAILLED: ' + HOST_TESTS[index]
+        sys.exit(0)
+    print 'TEST PASSED ' + str(len(test_results)) + ' tests were run'
+
+    dist_dir = get_android_dist_dir_or_die()
+    log_output_path = os.path.join(dist_dir, 'gtest/coverage')
+    cmd_path = os.path.join(get_android_root_or_die(), 'system/bt/test')
+    print cmd_path
+    cmd = [
+        os.path.join(cmd_path, 'gen_coverage.py'),
+        '--skip-html',
+        '--json-file',
+        '-o',
+        log_output_path,
+    ]
+    subprocess.call(cmd)
+
     sys.exit(0)
-  print 'TEST PASSED ' + str(len(test_results)) + ' tests were run'
-
-  dist_dir = get_android_dist_dir_or_die()
-  log_output_path = os.path.join(dist_dir, 'gtest/coverage')
-  cmd_path = os.path.join(get_android_root_or_die(), 'system/bt/test')
-  print cmd_path
-  cmd = [
-    os.path.join(cmd_path, 'gen_coverage.py'),
-    '--skip-html',
-    '--json-file',
-    '-o',
-    log_output_path,
-  ]
-  subprocess.call(cmd)
-
-  sys.exit(0)
 
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/test/run_unit_tests.sh b/test/run_unit_tests.sh
index 8041afc..754d9c5 100755
--- a/test/run_unit_tests.sh
+++ b/test/run_unit_tests.sh
@@ -12,6 +12,7 @@
   net_test_bta
   net_test_btif
   net_test_btif_profile_queue
+  net_test_btif_config_cache
   net_test_device
   net_test_hci
   net_test_stack
diff --git a/test/suite/adapter/adapter_unittest.cc b/test/suite/adapter/adapter_unittest.cc
index 7a26e28..2758046 100644
--- a/test/suite/adapter/adapter_unittest.cc
+++ b/test/suite/adapter/adapter_unittest.cc
@@ -179,7 +179,7 @@
   ASSERT_TRUE(bt_callbacks != nullptr);
 
   for (int i = 0; i < kTestRepeatCount; ++i) {
-    bt_interface()->init(bt_callbacks, false, false);
+    bt_interface()->init(bt_callbacks, false, false, 0, nullptr);
     EXPECT_EQ(bt_interface()->enable(), BT_STATUS_SUCCESS);
     semaphore_wait(adapter_state_changed_callback_sem_);
     EXPECT_EQ(GetState(), BT_STATE_ON) << "Adapter did not turn on.";
diff --git a/tools/scripts/btsnoop_live.py b/tools/scripts/btsnoop_live.py
index 4e145ff..a04dc19 100644
--- a/tools/scripts/btsnoop_live.py
+++ b/tools/scripts/btsnoop_live.py
@@ -67,19 +67,17 @@
 from _ctypes import FreeLibrary
 from datetime import datetime
 
-
 # Update below to right path corresponding to your machine, FTS version and OS used.
 FTS_INI_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 15.12\\'
 FTS_DLL_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 15.12\\Executables\\Core\\'
 
-
 iniName = 'liveimport.ini'
 if (platform.architecture()[0] == '32bit'):
     dllName = 'LiveImportAPI.dll'
 else:
     dllName = 'LiveImportAPI_x64.dll'
 
-launchFtsCmd = '\"'+FTS_DLL_PATH + 'fts.exe\"' + ' \"/ComProbe Protocol Analysis System=Generic\"' + ' \"/oemkey=Virtual\"'
+launchFtsCmd = '\"' + FTS_DLL_PATH + 'fts.exe\"' + ' \"/ComProbe Protocol Analysis System=Generic\"' + ' \"/oemkey=Virtual\"'
 
 # Unix Epoch delta since 01/01/1970
 FILETIME_EPOCH_DELTA = 116444736000000000
@@ -135,6 +133,7 @@
     else:
         return None
 
+
 def check_live_import_connection(live_import):
     """
     Launch FTS app in Virtual Sniffing Mode
@@ -146,7 +145,7 @@
 
     status = live_import.IsAppReady(byref(is_connection_running))
     if (is_connection_running.value == True):
-        print ("FTS is already launched, Start capture if not already started")
+        print("FTS is already launched, Start capture if not already started")
         return True
 
     print("Launching FTS Virtual Sniffing")
@@ -159,14 +158,14 @@
     while (is_connection_running.value == False and count < 12):
         status = live_import.IsAppReady(byref(is_connection_running))
         if (status < 0):
-            print("Live Import Internal Error %d" %(status))
+            print("Live Import Internal Error %d" % (status))
             return False
         if (is_connection_running.value == False):
             print("Waiting for 5 sec.. Open FTS Virtual Sniffing")
             time.sleep(5)
             count += 1
     if (is_connection_running.value == True):
-        print ("FTS is ready to receive the data, Start capture now")
+        print("FTS is ready to receive the data, Start capture now")
         return True
     else:
         print("FTS Virtual Sniffing didn't start until 1 min.. exiting")
@@ -184,13 +183,12 @@
         return None
 
     if live_import is None:
-        print("Error: Path to LiveImportAPI.dll is incorrect.. exiting");
+        print("Error: Path to LiveImportAPI.dll is incorrect.. exiting")
         return None
 
     print(dllName + " loaded successfully")
-    result = live_import.InitializeLiveImport(conn_str.encode('ascii', 'ignore'),
-                                              config_str.encode('ascii', 'ignore'),
-                                              byref(success))
+    result = live_import.InitializeLiveImport(
+        conn_str.encode('ascii', 'ignore'), config_str.encode('ascii', 'ignore'), byref(success))
     if (result < 0):
         print("Live Import Init failed")
         return None
@@ -244,7 +242,7 @@
 
     while True:
         try:
-            snoop_hdr  = btsnoop_sock.recv(SNOOP_HDR)
+            snoop_hdr = btsnoop_sock.recv(SNOOP_HDR)
             if snoop_hdr is not None:
                 try:
                     olen, ilen, flags = struct.unpack(">LLL", snoop_hdr[0:12])
@@ -261,7 +259,7 @@
                     if data_frag is not None:
                         snoop_data += data_frag
 
-                print ("Bytes received %d Olen %d ilen %d flags %d" % (len(snoop_data), olen, ilen, flags))
+                print("Bytes received %d Olen %d ilen %d flags %d" % (len(snoop_data), olen, ilen, flags))
                 packet_type = struct.unpack(">B", snoop_data[0:1])[0]
                 if packet_type == 1:
                     drf = 1
@@ -282,7 +280,7 @@
                     drf = 8
                     isend = 1
 
-                result = live_import.SendFrame(olen-1, olen-1, snoop_data[1:olen], drf, isend, timestamp)
+                result = live_import.SendFrame(olen - 1, olen - 1, snoop_data[1:olen], drf, isend, timestamp)
                 if (result < 0):
                     print("Send frame failed")
         except KeyboardInterrupt:
@@ -294,4 +292,3 @@
 
 if __name__ == '__main__':
     main()
-
diff --git a/tools/scripts/btsnooz.py b/tools/scripts/btsnooz.py
index 6e0e112..5f68f01 100755
--- a/tools/scripts/btsnooz.py
+++ b/tools/scripts/btsnooz.py
@@ -21,14 +21,12 @@
 the btsnoop headers.
 """
 
-
 import base64
 import fileinput
 import struct
 import sys
 import zlib
 
-
 # Enumeration of the values the 'type' field can take in a btsnooz
 # header. These values come from the Bluetooth stack's internal
 # representation of packet types.
@@ -41,127 +39,127 @@
 
 
 def type_to_direction(type):
-  """
+    """
   Returns the inbound/outbound direction of a packet given its type.
   0 = sent packet
   1 = received packet
   """
-  if type in [TYPE_IN_EVT, TYPE_IN_ACL, TYPE_IN_SCO]:
-    return 1
-  return 0
+    if type in [TYPE_IN_EVT, TYPE_IN_ACL, TYPE_IN_SCO]:
+        return 1
+    return 0
 
 
 def type_to_hci(type):
-  """
+    """
   Returns the HCI type of a packet given its btsnooz type.
   """
-  if type == TYPE_OUT_CMD:
-    return '\x01'
-  if type == TYPE_IN_ACL or type == TYPE_OUT_ACL:
-    return '\x02'
-  if type == TYPE_IN_SCO or type == TYPE_OUT_SCO:
-    return '\x03'
-  if type == TYPE_IN_EVT:
-    return '\x04'
+    if type == TYPE_OUT_CMD:
+        return '\x01'
+    if type == TYPE_IN_ACL or type == TYPE_OUT_ACL:
+        return '\x02'
+    if type == TYPE_IN_SCO or type == TYPE_OUT_SCO:
+        return '\x03'
+    if type == TYPE_IN_EVT:
+        return '\x04'
 
 
 def decode_snooz(snooz):
-  """
+    """
   Decodes all known versions of a btsnooz file into a btsnoop file.
   """
-  version, last_timestamp_ms = struct.unpack_from('=bQ', snooz)
+    version, last_timestamp_ms = struct.unpack_from('=bQ', snooz)
 
-  if version != 1 and version != 2:
-    sys.stderr.write('Unsupported btsnooz version: %s\n' % version)
-    exit(1)
+    if version != 1 and version != 2:
+        sys.stderr.write('Unsupported btsnooz version: %s\n' % version)
+        exit(1)
 
-  # Oddly, the file header (9 bytes) is not compressed, but the rest is.
-  decompressed = zlib.decompress(snooz[9:])
+    # Oddly, the file header (9 bytes) is not compressed, but the rest is.
+    decompressed = zlib.decompress(snooz[9:])
 
-  sys.stdout.write('btsnoop\x00\x00\x00\x00\x01\x00\x00\x03\xea')
+    sys.stdout.write('btsnoop\x00\x00\x00\x00\x01\x00\x00\x03\xea')
 
-  if version == 1:
-    decode_snooz_v1(decompressed, last_timestamp_ms)
-  elif version == 2:
-    decode_snooz_v2(decompressed, last_timestamp_ms)
+    if version == 1:
+        decode_snooz_v1(decompressed, last_timestamp_ms)
+    elif version == 2:
+        decode_snooz_v2(decompressed, last_timestamp_ms)
 
 
 def decode_snooz_v1(decompressed, last_timestamp_ms):
-  """
+    """
   Decodes btsnooz v1 files into a btsnoop file.
   """
-  # An unfortunate consequence of the file format design: we have to do a
-  # pass of the entire file to determine the timestamp of the first packet.
-  first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
-  offset = 0
-  while offset < len(decompressed):
-    length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
-    offset += 7 + length - 1
-    first_timestamp_ms -= delta_time_ms
+    # An unfortunate consequence of the file format design: we have to do a
+    # pass of the entire file to determine the timestamp of the first packet.
+    first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
+    offset = 0
+    while offset < len(decompressed):
+        length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
+        offset += 7 + length - 1
+        first_timestamp_ms -= delta_time_ms
 
-  # Second pass does the actual writing out to stdout.
-  offset = 0
-  while offset < len(decompressed):
-    length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
-    first_timestamp_ms += delta_time_ms
-    offset += 7
-    sys.stdout.write(struct.pack('>II', length, length))
-    sys.stdout.write(struct.pack('>II', type_to_direction(type), 0))
-    sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
-    sys.stdout.write(type_to_hci(type))
-    sys.stdout.write(decompressed[offset : offset + length - 1])
-    offset += length - 1
+    # Second pass does the actual writing out to stdout.
+    offset = 0
+    while offset < len(decompressed):
+        length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
+        first_timestamp_ms += delta_time_ms
+        offset += 7
+        sys.stdout.write(struct.pack('>II', length, length))
+        sys.stdout.write(struct.pack('>II', type_to_direction(type), 0))
+        sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
+        sys.stdout.write(type_to_hci(type))
+        sys.stdout.write(decompressed[offset:offset + length - 1])
+        offset += length - 1
 
 
 def decode_snooz_v2(decompressed, last_timestamp_ms):
-  """
+    """
   Decodes btsnooz v2 files into a btsnoop file.
   """
-  # An unfortunate consequence of the file format design: we have to do a
-  # pass of the entire file to determine the timestamp of the first packet.
-  first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
-  offset = 0
-  while offset < len(decompressed):
-    length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
-    offset += 9 + length - 1
-    first_timestamp_ms -= delta_time_ms
+    # An unfortunate consequence of the file format design: we have to do a
+    # pass of the entire file to determine the timestamp of the first packet.
+    first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
+    offset = 0
+    while offset < len(decompressed):
+        length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
+        offset += 9 + length - 1
+        first_timestamp_ms -= delta_time_ms
 
-  # Second pass does the actual writing out to stdout.
-  offset = 0
-  while offset < len(decompressed):
-    length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
-    first_timestamp_ms += delta_time_ms
-    offset += 9
-    sys.stdout.write(struct.pack('>II', packet_length, length))
-    sys.stdout.write(struct.pack('>II', type_to_direction(snooz_type), 0))
-    sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
-    sys.stdout.write(type_to_hci(snooz_type))
-    sys.stdout.write(decompressed[offset : offset + length - 1])
-    offset += length - 1
+    # Second pass does the actual writing out to stdout.
+    offset = 0
+    while offset < len(decompressed):
+        length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
+        first_timestamp_ms += delta_time_ms
+        offset += 9
+        sys.stdout.write(struct.pack('>II', packet_length, length))
+        sys.stdout.write(struct.pack('>II', type_to_direction(snooz_type), 0))
+        sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
+        sys.stdout.write(type_to_hci(snooz_type))
+        sys.stdout.write(decompressed[offset:offset + length - 1])
+        offset += length - 1
 
 
 def main():
-  if len(sys.argv) > 2:
-    sys.stderr.write('Usage: %s [bugreport]\n' % sys.argv[0])
-    exit(1)
+    if len(sys.argv) > 2:
+        sys.stderr.write('Usage: %s [bugreport]\n' % sys.argv[0])
+        exit(1)
 
-  iterator = fileinput.input()
-  found = False
-  base64_string = ""
-  for line in iterator:
-    if found:
-      if line.find('--- END:BTSNOOP_LOG_SUMMARY') != -1:
-        decode_snooz(base64.standard_b64decode(base64_string))
-        sys.exit(0)
-      base64_string += line.strip()
+    iterator = fileinput.input()
+    found = False
+    base64_string = ""
+    for line in iterator:
+        if found:
+            if line.find('--- END:BTSNOOP_LOG_SUMMARY') != -1:
+                decode_snooz(base64.standard_b64decode(base64_string))
+                sys.exit(0)
+            base64_string += line.strip()
 
-    if line.find('--- BEGIN:BTSNOOP_LOG_SUMMARY') != -1:
-      found = True
+        if line.find('--- BEGIN:BTSNOOP_LOG_SUMMARY') != -1:
+            found = True
 
-  if not found:
-    sys.stderr.write('No btsnooz section found in bugreport.\n')
-    sys.exit(1)
+    if not found:
+        sys.stderr.write('No btsnooz section found in bugreport.\n')
+        sys.exit(1)
 
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/tools/scripts/dump_hearingaid_audio.py b/tools/scripts/dump_hearingaid_audio.py
index ca2eed2..fd3f064 100755
--- a/tools/scripts/dump_hearingaid_audio.py
+++ b/tools/scripts/dump_hearingaid_audio.py
@@ -54,18 +54,12 @@
 AUDIO_DATA_B = "AUDIO_DATA_B"
 
 # Debug packet header struct
-header_list_str = ["Event Processed",
-                   "Number Packet Nacked By Slave",
-                   "Number Packet Nacked By Master"]
+header_list_str = ["Event Processed", "Number Packet Nacked By Slave", "Number Packet Nacked By Master"]
 # Debug frame information structs
-data_list_str = ["Event Number",
-                 "Overrun",
-                 "Underrun",
-                 "Skips",
-                 "Rendered Audio Frame",
-                 "First PDU Option",
-                 "Second PDU Option",
-                 "Third PDU Option"]
+data_list_str = [
+    "Event Number", "Overrun", "Underrun", "Skips", "Rendered Audio Frame", "First PDU Option", "Second PDU Option",
+    "Third PDU Option"
+]
 
 AUDIO_CONTROL_POINT_UUID = "f0d4de7e4a88476c9d9f1937b0996cc0"
 SEC_CONVERT = 1000000
@@ -86,172 +80,168 @@
 # Parse Hearing Aid Packet
 #-----------------------------------------------------------------------
 
+
 def parse_acl_ha_debug_buffer(data, result):
-  """This function extracts HA debug buffer"""
-  if len(data) < 5:
-    return
+    """This function extracts HA debug buffer"""
+    if len(data) < 5:
+        return
 
-  version, data = unpack_data(data, 1)
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], DEBUG_VERSION, str(version))
+    version, data = unpack_data(data, 1)
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], DEBUG_VERSION, str(version))
 
-  debug_str = result[TIMESTAMP_TIME_FORMAT];
-  for p in range(3):
-    byte_data, data = unpack_data(data, 1)
-    debug_str = debug_str + ", " + header_list_str[p] + "=" + str(byte_data).rjust(3)
-
-  if full_debug:
-    debug_str = debug_str + "\n" + "|".join(data_list_str) + "\n"
-    while True:
-      if len(data) < 7:
-        break
-      base = 0
-      data_list_content = []
-      for counter in range(6):
-        p = base + counter
+    debug_str = result[TIMESTAMP_TIME_FORMAT]
+    for p in range(3):
         byte_data, data = unpack_data(data, 1)
-        if p == 1:
-          data_list_content.append(str(byte_data & 0x03).rjust(len(data_list_str[p])))
-          data_list_content.append(str((byte_data >> 2) & 0x03).rjust(len(data_list_str[p + 1])))
-          data_list_content.append(str((byte_data >> 4) & 0x0f).rjust(len(data_list_str[p + 2])))
-          base = 2
-        else:
-          data_list_content.append(str(byte_data).rjust(len(data_list_str[p])))
-      debug_str = debug_str + "|".join(data_list_content) + "\n"
+        debug_str = debug_str + ", " + header_list_str[p] + "=" + str(byte_data).rjust(3)
 
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], DEBUG_DATA, debug_str)
+    if full_debug:
+        debug_str = debug_str + "\n" + "|".join(data_list_str) + "\n"
+        while True:
+            if len(data) < 7:
+                break
+            base = 0
+            data_list_content = []
+            for counter in range(6):
+                p = base + counter
+                byte_data, data = unpack_data(data, 1)
+                if p == 1:
+                    data_list_content.append(str(byte_data & 0x03).rjust(len(data_list_str[p])))
+                    data_list_content.append(str((byte_data >> 2) & 0x03).rjust(len(data_list_str[p + 1])))
+                    data_list_content.append(str((byte_data >> 4) & 0x0f).rjust(len(data_list_str[p + 2])))
+                    base = 2
+                else:
+                    data_list_content.append(str(byte_data).rjust(len(data_list_str[p])))
+            debug_str = debug_str + "|".join(data_list_content) + "\n"
+
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], DEBUG_DATA, debug_str)
+
 
 def parse_acl_ha_audio_data(data, result):
-  """This function extracts HA audio data."""
-  if len(data) < 2:
-    return
-  # Remove audio packet number
-  audio_data_b = data[1:]
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                    AUDIO_DATA_B, audio_data_b)
+    """This function extracts HA audio data."""
+    if len(data) < 2:
+        return
+    # Remove audio packet number
+    audio_data_b = data[1:]
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], AUDIO_DATA_B, audio_data_b)
 
 
 def parse_acl_ha_audio_type(data, result):
-  """This function parses HA audio control cmd audio type."""
-  audio_type, data = unpack_data(data, 1)
-  if audio_type is None:
-    return
-  elif audio_type == 0x01:
-    audio_type = "Ringtone"
-  elif audio_type == 0x02:
-    audio_type = "Phonecall"
-  elif audio_type == 0x03:
-    audio_type = "Media"
-  else:
-    audio_type = "Unknown"
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                    AUDIO_TYPE, audio_type)
+    """This function parses HA audio control cmd audio type."""
+    audio_type, data = unpack_data(data, 1)
+    if audio_type is None:
+        return
+    elif audio_type == 0x01:
+        audio_type = "Ringtone"
+    elif audio_type == 0x02:
+        audio_type = "Phonecall"
+    elif audio_type == 0x03:
+        audio_type = "Media"
+    else:
+        audio_type = "Unknown"
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], AUDIO_TYPE, audio_type)
 
 
 def parse_acl_ha_codec(data, result):
-  """This function parses HA audio control cmd codec and sample rate."""
-  codec, data = unpack_data(data, 1)
-  if codec == 0x01:
-    codec = "G722"
-    sample_rate = "16KHZ"
-  elif codec == 0x02:
-    codec = "G722"
-    sample_rate = "24KHZ"
-  else:
-    codec = "Unknown"
-    sample_rate = "Unknown"
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                    CODEC, codec)
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                    SAMPLE_RATE, sample_rate)
-  parse_acl_ha_audio_type(data, result)
+    """This function parses HA audio control cmd codec and sample rate."""
+    codec, data = unpack_data(data, 1)
+    if codec == 0x01:
+        codec = "G722"
+        sample_rate = "16KHZ"
+    elif codec == 0x02:
+        codec = "G722"
+        sample_rate = "24KHZ"
+    else:
+        codec = "Unknown"
+        sample_rate = "Unknown"
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], CODEC, codec)
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], SAMPLE_RATE, sample_rate)
+    parse_acl_ha_audio_type(data, result)
 
 
 def parse_acl_ha_audio_control_cmd(data, result):
-  """This function parses HA audio control cmd is start/stop."""
-  control_cmd, data = unpack_data(data, 1)
-  if control_cmd == 0x01:
-    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                      START, True)
-    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                      TIMESTAMP_STR_FORMAT, result[TIMESTAMP_STR_FORMAT])
-    parse_acl_ha_codec(data, result)
-  elif control_cmd == 0x02:
-    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                      START, False)
+    """This function parses HA audio control cmd is start/stop."""
+    control_cmd, data = unpack_data(data, 1)
+    if control_cmd == 0x01:
+        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], START, True)
+        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], TIMESTAMP_STR_FORMAT,
+                          result[TIMESTAMP_STR_FORMAT])
+        parse_acl_ha_codec(data, result)
+    elif control_cmd == 0x02:
+        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], START, False)
 
 
 #-----------------------------------------------------------------------
 # Parse ACL Packet
 #-----------------------------------------------------------------------
 
+
 def parse_acl_att_long_uuid(data, result):
-  """This function parses ATT long UUID to get attr_handle."""
-  # len (1 byte) + start_attr_handle (2 bytes) + properties (1 byte) +
-  # attr_handle (2 bytes) + long_uuid (16 bytes) = 22 bytes
-  if len(data) < 22:
-    return
-  # skip unpack len, start_attr_handle, properties.
-  data = data[4:]
-  attr_handle, data = unpack_data(data, 2)
-  long_uuid_list = []
-  for p in range(0, 16):
-    long_uuid_list.append("{0:02x}".format(struct.unpack(">B", data[p])[0]))
-  long_uuid_list.reverse()
-  long_uuid = "".join(long_uuid_list)
-  # Check long_uuid is AUDIO_CONTROL_POINT uuid to get the attr_handle.
-  if long_uuid == AUDIO_CONTROL_POINT_UUID:
-    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                      AUDIO_CONTROL_ATTR_HANDLE, attr_handle)
+    """This function parses ATT long UUID to get attr_handle."""
+    # len (1 byte) + start_attr_handle (2 bytes) + properties (1 byte) +
+    # attr_handle (2 bytes) + long_uuid (16 bytes) = 22 bytes
+    if len(data) < 22:
+        return
+    # skip unpack len, start_attr_handle, properties.
+    data = data[4:]
+    attr_handle, data = unpack_data(data, 2)
+    long_uuid_list = []
+    for p in range(0, 16):
+        long_uuid_list.append("{0:02x}".format(struct.unpack(">B", data[p])[0]))
+    long_uuid_list.reverse()
+    long_uuid = "".join(long_uuid_list)
+    # Check long_uuid is AUDIO_CONTROL_POINT uuid to get the attr_handle.
+    if long_uuid == AUDIO_CONTROL_POINT_UUID:
+        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], AUDIO_CONTROL_ATTR_HANDLE, attr_handle)
 
 
 def parse_acl_opcode(data, result):
-  """This function parses acl data opcode."""
-  # opcode (1 byte) = 1 bytes
-  if len(data) < 1:
-    return
-  opcode, data = unpack_data(data, 1)
-  # Check opcode is 0x12 (write request) and attr_handle is
-  # audio_control_attr_handle for check it is HA audio control cmd.
-  if result[IS_SENT] and opcode == 0x12:
-    if len(data) < 2:
-      return
-    attr_handle, data = unpack_data(data, 2)
-    if attr_handle == \
-        get_audio_control_attr_handle(result[CONNECTION_HANDLE]):
-          parse_acl_ha_audio_control_cmd(data, result)
-  # Check opcode is 0x09 (read response) to parse ATT long UUID.
-  elif not result[IS_SENT] and opcode == 0x09:
-    parse_acl_att_long_uuid(data, result)
+    """This function parses acl data opcode."""
+    # opcode (1 byte) = 1 bytes
+    if len(data) < 1:
+        return
+    opcode, data = unpack_data(data, 1)
+    # Check opcode is 0x12 (write request) and attr_handle is
+    # audio_control_attr_handle for check it is HA audio control cmd.
+    if result[IS_SENT] and opcode == 0x12:
+        if len(data) < 2:
+            return
+        attr_handle, data = unpack_data(data, 2)
+        if attr_handle == \
+            get_audio_control_attr_handle(result[CONNECTION_HANDLE]):
+            parse_acl_ha_audio_control_cmd(data, result)
+    # Check opcode is 0x09 (read response) to parse ATT long UUID.
+    elif not result[IS_SENT] and opcode == 0x09:
+        parse_acl_att_long_uuid(data, result)
 
 
 def parse_acl_handle(data, result):
-  """This function parses acl data handle."""
-  # connection_handle (2 bytes) + total_len (2 bytes) + pdu (2 bytes)
-  # + channel_id (2 bytes) = 8 bytes
-  if len(data) < 8:
-    return
-  connection_handle, data = unpack_data(data, 2)
-  connection_handle = connection_handle & 0x0FFF
-  # skip unpack total_len
-  data = data[2:]
-  pdu, data = unpack_data(data, 2)
-  channel_id, data = unpack_data(data, 2)
+    """This function parses acl data handle."""
+    # connection_handle (2 bytes) + total_len (2 bytes) + pdu (2 bytes)
+    # + channel_id (2 bytes) = 8 bytes
+    if len(data) < 8:
+        return
+    connection_handle, data = unpack_data(data, 2)
+    connection_handle = connection_handle & 0x0FFF
+    # skip unpack total_len
+    data = data[2:]
+    pdu, data = unpack_data(data, 2)
+    channel_id, data = unpack_data(data, 2)
 
-  # Check ATT packet or "Coc Data Packet" to get ATT information and audio
-  # data.
-  if connection_handle <= 0x0EFF:
-    if channel_id <= 0x003F:
-      result[CONNECTION_HANDLE] = connection_handle
-      parse_acl_opcode(data, result)
-    elif channel_id >= 0x0040 and channel_id <= 0x007F:
-      result[CONNECTION_HANDLE] = connection_handle
-      sdu, data = unpack_data(data, 2)
-      if pdu - 2 == sdu:
-        if result[IS_SENT]:
-          parse_acl_ha_audio_data(data, result)
-        else:
-          if simple_debug:
-            parse_acl_ha_debug_buffer(data, result)
+    # Check ATT packet or "Coc Data Packet" to get ATT information and audio
+    # data.
+    if connection_handle <= 0x0EFF:
+        if channel_id <= 0x003F:
+            result[CONNECTION_HANDLE] = connection_handle
+            parse_acl_opcode(data, result)
+        elif channel_id >= 0x0040 and channel_id <= 0x007F:
+            result[CONNECTION_HANDLE] = connection_handle
+            sdu, data = unpack_data(data, 2)
+            if pdu - 2 == sdu:
+                if result[IS_SENT]:
+                    parse_acl_ha_audio_data(data, result)
+                else:
+                    if simple_debug:
+                        parse_acl_ha_debug_buffer(data, result)
 
 
 #=======================================================================
@@ -260,48 +250,46 @@
 
 
 def parse_hci_evt_peer_address(data, result):
-  """This function parses peer address from hci event."""
-  peer_address_list = []
-  address_empty_list = ["00", "00", "00", "00", "00", "00"]
-  for n in range(0, 3):
-    if len(data) < 6:
-      return
-    for p in range(0, 6):
-      peer_address_list.append("{0:02x}".format(struct.unpack(">B",
-                                                              data[p])[0]))
-    # Check the address is empty or not.
-    if peer_address_list == address_empty_list:
-      del peer_address_list[:]
-      data = data[6:]
-    else:
-      break
-  peer_address_list.reverse()
-  peer_address = "_".join(peer_address_list)
-  update_audio_data("", "", PEER_ADDRESS, peer_address)
-  update_audio_data(PEER_ADDRESS, peer_address, CONNECTION_HANDLE,
-                    result[CONNECTION_HANDLE])
+    """This function parses peer address from hci event."""
+    peer_address_list = []
+    address_empty_list = ["00", "00", "00", "00", "00", "00"]
+    for n in range(0, 3):
+        if len(data) < 6:
+            return
+        for p in range(0, 6):
+            peer_address_list.append("{0:02x}".format(struct.unpack(">B", data[p])[0]))
+        # Check the address is empty or not.
+        if peer_address_list == address_empty_list:
+            del peer_address_list[:]
+            data = data[6:]
+        else:
+            break
+    peer_address_list.reverse()
+    peer_address = "_".join(peer_address_list)
+    update_audio_data("", "", PEER_ADDRESS, peer_address)
+    update_audio_data(PEER_ADDRESS, peer_address, CONNECTION_HANDLE, result[CONNECTION_HANDLE])
 
 
 def parse_hci_evt_code(data, result):
-  """This function parses hci event content."""
-  # hci_evt (1 byte) + param_total_len (1 byte) + sub_event (1 byte)
-  # + status (1 byte) + connection_handle (2 bytes) + role (1 byte)
-  # + address_type (1 byte) = 8 bytes
-  if len(data) < 8:
-    return
-  hci_evt, data = unpack_data(data, 1)
-  # skip unpack param_total_len.
-  data = data[1:]
-  sub_event, data = unpack_data(data, 1)
-  status, data = unpack_data(data, 1)
-  connection_handle, data = unpack_data(data, 2)
-  connection_handle = connection_handle & 0x0FFF
-  # skip unpack role, address_type.
-  data = data[2:]
-  # We will directly check it is LE Enhanced Connection Complete or not
-  # for get Connection Handle and Address.
-  if not result[IS_SENT] and hci_evt == 0x3E and sub_event == 0x0A \
-      and status == 0x00 and connection_handle <= 0x0EFF:
+    """This function parses hci event content."""
+    # hci_evt (1 byte) + param_total_len (1 byte) + sub_event (1 byte)
+    # + status (1 byte) + connection_handle (2 bytes) + role (1 byte)
+    # + address_type (1 byte) = 8 bytes
+    if len(data) < 8:
+        return
+    hci_evt, data = unpack_data(data, 1)
+    # skip unpack param_total_len.
+    data = data[1:]
+    sub_event, data = unpack_data(data, 1)
+    status, data = unpack_data(data, 1)
+    connection_handle, data = unpack_data(data, 2)
+    connection_handle = connection_handle & 0x0FFF
+    # skip unpack role, address_type.
+    data = data[2:]
+    # We will directly check it is LE Enhanced Connection Complete or not
+    # for get Connection Handle and Address.
+    if not result[IS_SENT] and hci_evt == 0x3E and sub_event == 0x0A \
+        and status == 0x00 and connection_handle <= 0x0EFF:
         result[CONNECTION_HANDLE] = connection_handle
         parse_hci_evt_peer_address(data, result)
 
@@ -312,41 +300,41 @@
 
 
 def parse_packet_data(data, result):
-  """This function parses packet type."""
-  packet_type, data = unpack_data(data, 1)
-  if packet_type == 0x02:
-    # Try to check HearingAid audio control packet and data packet.
-    parse_acl_handle(data, result)
-  elif packet_type == 0x04:
-    # Try to check HearingAid connection successful packet.
-    parse_hci_evt_code(data, result)
+    """This function parses packet type."""
+    packet_type, data = unpack_data(data, 1)
+    if packet_type == 0x02:
+        # Try to check HearingAid audio control packet and data packet.
+        parse_acl_handle(data, result)
+    elif packet_type == 0x04:
+        # Try to check HearingAid connection successful packet.
+        parse_hci_evt_code(data, result)
 
 
 def parse_packet(btsnoop_file):
-  """This function parses packet len, timestamp."""
-  packet_result = {}
+    """This function parses packet len, timestamp."""
+    packet_result = {}
 
-  # ori_len (4 bytes) + include_len (4 bytes) + packet_flag (4 bytes)
-  # + drop (4 bytes) + timestamp (8 bytes) = 24 bytes
-  packet_header = btsnoop_file.read(24)
-  if len(packet_header) != 24:
-    return False
+    # ori_len (4 bytes) + include_len (4 bytes) + packet_flag (4 bytes)
+    # + drop (4 bytes) + timestamp (8 bytes) = 24 bytes
+    packet_header = btsnoop_file.read(24)
+    if len(packet_header) != 24:
+        return False
 
-  ori_len, include_len, packet_flag, drop, timestamp = \
-      struct.unpack(">IIIIq", packet_header)
+    ori_len, include_len, packet_flag, drop, timestamp = \
+        struct.unpack(">IIIIq", packet_header)
 
-  if ori_len == include_len:
-    packet_data = btsnoop_file.read(ori_len)
-    if len(packet_data) != ori_len:
-      return False
-    if packet_flag != 2 and drop == 0:
-      packet_result[IS_SENT] = (packet_flag == 0)
-      packet_result[TIMESTAMP_STR_FORMAT], packet_result[TIMESTAMP_TIME_FORMAT] = convert_time_str(timestamp)
-      parse_packet_data(packet_data, packet_result)
-  else:
-    return False
+    if ori_len == include_len:
+        packet_data = btsnoop_file.read(ori_len)
+        if len(packet_data) != ori_len:
+            return False
+        if packet_flag != 2 and drop == 0:
+            packet_result[IS_SENT] = (packet_flag == 0)
+            packet_result[TIMESTAMP_STR_FORMAT], packet_result[TIMESTAMP_TIME_FORMAT] = convert_time_str(timestamp)
+            parse_packet_data(packet_data, packet_result)
+    else:
+        return False
 
-  return True
+    return True
 
 
 #=======================================================================
@@ -355,49 +343,48 @@
 
 
 def dump_audio_data(data):
-  """This function dumps audio data into file."""
-  file_type = "." + data[CODEC]
-  file_name_list = []
-  file_name_list.append(data[PEER_ADDRESS])
-  file_name_list.append(data[TIMESTAMP_STR_FORMAT])
-  file_name_list.append(data[AUDIO_TYPE])
-  file_name_list.append(data[SAMPLE_RATE])
-  if folder is not None:
-    if not os.path.exists(folder):
-      os.makedirs(folder)
-    audio_file_name = os.path.join(folder, "-".join(file_name_list) + file_type)
-    if data.has_key(DEBUG_VERSION):
-      file_prefix = "debug_ver_" + data[DEBUG_VERSION] + "-"
-      debug_file_name = os.path.join(folder, file_prefix + "-".join(file_name_list) + ".txt")
-  else:
-    audio_file_name = "-".join(file_name_list) + file_type
-    if data.has_key(DEBUG_VERSION):
-      file_prefix = "debug_ver_" + data[DEBUG_VERSION] + "-"
-      debug_file_name = file_prefix + "-".join(file_name_list) + ".txt"
-
-  sys.stdout.write("Start to dump Audio File : %s\n" % audio_file_name)
-  if data.has_key(AUDIO_DATA_B):
-    with open(audio_file_name, "wb+") as audio_file:
-      audio_file.write(data[AUDIO_DATA_B])
-      sys.stdout.write("Finished to dump Audio File: %s\n\n" % audio_file_name)
-  else:
-    sys.stdout.write("Fail to dump Audio File: %s\n" % audio_file_name)
-    sys.stdout.write("There isn't any Hearing Aid audio data.\n\n")
-
-  if simple_debug:
-    sys.stdout.write("Start to dump audio %s Debug File\n" % audio_file_name)
-    if data.has_key(DEBUG_DATA):
-      with open(debug_file_name, "wb+") as debug_file:
-        debug_file.write(data[DEBUG_DATA])
-        sys.stdout.write("Finished to dump Debug File: %s\n\n" % debug_file_name)
+    """This function dumps audio data into file."""
+    file_type = "." + data[CODEC]
+    file_name_list = []
+    file_name_list.append(data[PEER_ADDRESS])
+    file_name_list.append(data[TIMESTAMP_STR_FORMAT])
+    file_name_list.append(data[AUDIO_TYPE])
+    file_name_list.append(data[SAMPLE_RATE])
+    if folder is not None:
+        if not os.path.exists(folder):
+            os.makedirs(folder)
+        audio_file_name = os.path.join(folder, "-".join(file_name_list) + file_type)
+        if data.has_key(DEBUG_VERSION):
+            file_prefix = "debug_ver_" + data[DEBUG_VERSION] + "-"
+            debug_file_name = os.path.join(folder, file_prefix + "-".join(file_name_list) + ".txt")
     else:
-      sys.stdout.write("Fail to dump audio %s Debug File\n" % audio_file_name)
-      sys.stdout.write("There isn't any Hearing Aid debug data.\n\n")
+        audio_file_name = "-".join(file_name_list) + file_type
+        if data.has_key(DEBUG_VERSION):
+            file_prefix = "debug_ver_" + data[DEBUG_VERSION] + "-"
+            debug_file_name = file_prefix + "-".join(file_name_list) + ".txt"
 
+    sys.stdout.write("Start to dump Audio File : %s\n" % audio_file_name)
+    if data.has_key(AUDIO_DATA_B):
+        with open(audio_file_name, "wb+") as audio_file:
+            audio_file.write(data[AUDIO_DATA_B])
+            sys.stdout.write("Finished to dump Audio File: %s\n\n" % audio_file_name)
+    else:
+        sys.stdout.write("Fail to dump Audio File: %s\n" % audio_file_name)
+        sys.stdout.write("There isn't any Hearing Aid audio data.\n\n")
+
+    if simple_debug:
+        sys.stdout.write("Start to dump audio %s Debug File\n" % audio_file_name)
+        if data.has_key(DEBUG_DATA):
+            with open(debug_file_name, "wb+") as debug_file:
+                debug_file.write(data[DEBUG_DATA])
+                sys.stdout.write("Finished to dump Debug File: %s\n\n" % debug_file_name)
+        else:
+            sys.stdout.write("Fail to dump audio %s Debug File\n" % audio_file_name)
+            sys.stdout.write("There isn't any Hearing Aid debug data.\n\n")
 
 
 def update_audio_data(relate_key, relate_value, key, value):
-  """
+    """
   This function records the dump audio file related information.
   audio_data = {
     PEER_ADDRESS:{
@@ -428,44 +415,44 @@
     }
   }
   """
-  if key == PEER_ADDRESS:
-    if audio_data.has_key(value):
-      # Dump audio data and clear previous data.
-      update_audio_data(key, value, START, False)
-      # Extra clear CONNECTION_HANDLE due to new connection create.
-      if audio_data[value].has_key(CONNECTION_HANDLE):
-        audio_data[value].pop(CONNECTION_HANDLE, "")
-    else:
-      device_audio_data = {key: value}
-      temp_audio_data = {value: device_audio_data}
-      audio_data.update(temp_audio_data)
-  else:
-    for i in audio_data:
-      if audio_data[i].has_key(relate_key) \
-          and audio_data[i][relate_key] == relate_value:
-            if key == START:
-              if audio_data[i].has_key(key) and audio_data[i][key]:
-                dump_audio_data(audio_data[i])
-              # Clear data except PEER_ADDRESS, CONNECTION_HANDLE and
-              # AUDIO_CONTROL_ATTR_HANDLE.
-              audio_data[i].pop(key, "")
-              audio_data[i].pop(TIMESTAMP_STR_FORMAT, "")
-              audio_data[i].pop(CODEC, "")
-              audio_data[i].pop(SAMPLE_RATE, "")
-              audio_data[i].pop(AUDIO_TYPE, "")
-              audio_data[i].pop(DEBUG_VERSION, "")
-              audio_data[i].pop(DEBUG_DATA, "")
-              audio_data[i].pop(AUDIO_DATA_B, "")
-            elif key == AUDIO_DATA_B or key == DEBUG_DATA:
-              if audio_data[i].has_key(START) and audio_data[i][START]:
-                if audio_data[i].has_key(key):
-                  ori_data = audio_data[i].pop(key, "")
-                  value = ori_data + value
-              else:
-                # Audio doesn't start, don't record.
-                return
+    if key == PEER_ADDRESS:
+        if audio_data.has_key(value):
+            # Dump audio data and clear previous data.
+            update_audio_data(key, value, START, False)
+            # Extra clear CONNECTION_HANDLE due to new connection create.
+            if audio_data[value].has_key(CONNECTION_HANDLE):
+                audio_data[value].pop(CONNECTION_HANDLE, "")
+        else:
             device_audio_data = {key: value}
-            audio_data[i].update(device_audio_data)
+            temp_audio_data = {value: device_audio_data}
+            audio_data.update(temp_audio_data)
+    else:
+        for i in audio_data:
+            if audio_data[i].has_key(relate_key) \
+                and audio_data[i][relate_key] == relate_value:
+                if key == START:
+                    if audio_data[i].has_key(key) and audio_data[i][key]:
+                        dump_audio_data(audio_data[i])
+                    # Clear data except PEER_ADDRESS, CONNECTION_HANDLE and
+                    # AUDIO_CONTROL_ATTR_HANDLE.
+                    audio_data[i].pop(key, "")
+                    audio_data[i].pop(TIMESTAMP_STR_FORMAT, "")
+                    audio_data[i].pop(CODEC, "")
+                    audio_data[i].pop(SAMPLE_RATE, "")
+                    audio_data[i].pop(AUDIO_TYPE, "")
+                    audio_data[i].pop(DEBUG_VERSION, "")
+                    audio_data[i].pop(DEBUG_DATA, "")
+                    audio_data[i].pop(AUDIO_DATA_B, "")
+                elif key == AUDIO_DATA_B or key == DEBUG_DATA:
+                    if audio_data[i].has_key(START) and audio_data[i][START]:
+                        if audio_data[i].has_key(key):
+                            ori_data = audio_data[i].pop(key, "")
+                            value = ori_data + value
+                    else:
+                        # Audio doesn't start, don't record.
+                        return
+                device_audio_data = {key: value}
+                audio_data[i].update(device_audio_data)
 
 
 #=======================================================================
@@ -474,159 +461,171 @@
 
 
 def get_audio_control_attr_handle(connection_handle):
-  """This function gets audio_control_attr_handle."""
-  # If force_audio_control_attr_handle is set, will use it first.
-  if force_audio_control_attr_handle is not None:
-    return force_audio_control_attr_handle
+    """This function gets audio_control_attr_handle."""
+    # If force_audio_control_attr_handle is set, will use it first.
+    if force_audio_control_attr_handle is not None:
+        return force_audio_control_attr_handle
 
-  # Try to check the audio_control_attr_handle is record into audio_data.
-  for i in audio_data:
-    if audio_data[i].has_key(CONNECTION_HANDLE) \
-        and audio_data[i][CONNECTION_HANDLE] == connection_handle:
-          if audio_data[i].has_key(AUDIO_CONTROL_ATTR_HANDLE):
-            return audio_data[i][AUDIO_CONTROL_ATTR_HANDLE]
+    # Try to check the audio_control_attr_handle is record into audio_data.
+    for i in audio_data:
+        if audio_data[i].has_key(CONNECTION_HANDLE) \
+            and audio_data[i][CONNECTION_HANDLE] == connection_handle:
+            if audio_data[i].has_key(AUDIO_CONTROL_ATTR_HANDLE):
+                return audio_data[i][AUDIO_CONTROL_ATTR_HANDLE]
 
-  # Return default attr_handle if audio_data doesn't record it.
-  return default_audio_control_attr_handle
+    # Return default attr_handle if audio_data doesn't record it.
+    return default_audio_control_attr_handle
 
 
 def unpack_data(data, byte):
-  """This function unpacks data."""
-  if byte == 1:
-    value = struct.unpack(">B", data[0])[0]
-  elif byte == 2:
-    value = struct.unpack(">H", data[1]+data[0])[0]
-  else:
-    value = ""
-  data = data[byte:]
-  return value, data
+    """This function unpacks data."""
+    if byte == 1:
+        value = struct.unpack(">B", data[0])[0]
+    elif byte == 2:
+        value = struct.unpack(">H", data[1] + data[0])[0]
+    else:
+        value = ""
+    data = data[byte:]
+    return value, data
 
 
 def convert_time_str(timestamp):
-  """This function converts time to string format."""
-  really_timestamp = float(timestamp) / SEC_CONVERT
-  local_timestamp = time.localtime(really_timestamp)
-  dt = really_timestamp - long(really_timestamp)
-  ms_str = "{0:06}".format(int(round(dt * 1000000)))
+    """This function converts time to string format."""
+    really_timestamp = float(timestamp) / SEC_CONVERT
+    local_timestamp = time.localtime(really_timestamp)
+    dt = really_timestamp - long(really_timestamp)
+    ms_str = "{0:06}".format(int(round(dt * 1000000)))
 
-  str_format = time.strftime("%m_%d__%H_%M_%S", local_timestamp)
-  full_str_format = str_format + "_" + ms_str
+    str_format = time.strftime("%m_%d__%H_%M_%S", local_timestamp)
+    full_str_format = str_format + "_" + ms_str
 
-  time_format = time.strftime("%m-%d %H:%M:%S", local_timestamp)
-  full_time_format = time_format + "." + ms_str
-  return full_str_format, full_time_format
+    time_format = time.strftime("%m-%d %H:%M:%S", local_timestamp)
+    full_time_format = time_format + "." + ms_str
+    return full_str_format, full_time_format
 
 
 def set_config():
-  """This function is for set config by flag and check the argv is correct."""
-  argv_parser = argparse.ArgumentParser(
-      description="Extracts Hearing Aid audio data from BTSNOOP.")
-  argv_parser.add_argument("BTSNOOP", help="BLUETOOTH BTSNOOP file.")
-  argv_parser.add_argument("-f", "--folder", help="select output folder.",
-                           dest="folder")
-  argv_parser.add_argument("-c1", "--connection-handle1",
-                           help="set a fake connection handle 1 to capture \
-                           audio dump.", dest="connection_handle1", type=int)
-  argv_parser.add_argument("-c2", "--connection-handle2",
-                           help="set a fake connection handle 2 to capture \
-                           audio dump.", dest="connection_handle2", type=int)
-  argv_parser.add_argument("-ns", "--no-start", help="No audio 'Start' cmd is \
+    """This function is for set config by flag and check the argv is correct."""
+    argv_parser = argparse.ArgumentParser(description="Extracts Hearing Aid audio data from BTSNOOP.")
+    argv_parser.add_argument("BTSNOOP", help="BLUETOOTH BTSNOOP file.")
+    argv_parser.add_argument("-f", "--folder", help="select output folder.", dest="folder")
+    argv_parser.add_argument(
+        "-c1",
+        "--connection-handle1",
+        help="set a fake connection handle 1 to capture \
+                           audio dump.",
+        dest="connection_handle1",
+        type=int)
+    argv_parser.add_argument(
+        "-c2",
+        "--connection-handle2",
+        help="set a fake connection handle 2 to capture \
+                           audio dump.",
+        dest="connection_handle2",
+        type=int)
+    argv_parser.add_argument(
+        "-ns",
+        "--no-start",
+        help="No audio 'Start' cmd is \
                            needed before extracting audio data.",
-                           dest="no_start", default="False")
-  argv_parser.add_argument("-dc", "--default-codec", help="set a default \
-                           codec.", dest="codec", default="G722")
-  argv_parser.add_argument("-a", "--attr-handle",
-                           help="force to select audio control attr handle.",
-                           dest="audio_control_attr_handle", type=int)
-  argv_parser.add_argument("-d", "--debug",
-                           help="dump full debug buffer content.",
-                           dest="full_debug", default="False")
-  argv_parser.add_argument("-sd", "--simple-debug",
-                           help="dump debug buffer header content.",
-                           dest="simple_debug", default="False")
-  arg = argv_parser.parse_args()
+        dest="no_start",
+        default="False")
+    argv_parser.add_argument(
+        "-dc",
+        "--default-codec",
+        help="set a default \
+                           codec.",
+        dest="codec",
+        default="G722")
+    argv_parser.add_argument(
+        "-a",
+        "--attr-handle",
+        help="force to select audio control attr handle.",
+        dest="audio_control_attr_handle",
+        type=int)
+    argv_parser.add_argument(
+        "-d", "--debug", help="dump full debug buffer content.", dest="full_debug", default="False")
+    argv_parser.add_argument(
+        "-sd", "--simple-debug", help="dump debug buffer header content.", dest="simple_debug", default="False")
+    arg = argv_parser.parse_args()
 
-  if arg.folder is not None:
-    global folder
-    folder = arg.folder
+    if arg.folder is not None:
+        global folder
+        folder = arg.folder
 
-  if arg.connection_handle1 is not None and arg.connection_handle2 is not None \
-      and arg.connection_handle1 == arg.connection_handle2:
+    if arg.connection_handle1 is not None and arg.connection_handle2 is not None \
+        and arg.connection_handle1 == arg.connection_handle2:
         argv_parser.error("connection_handle1 can't be same with \
                           connection_handle2")
         exit(1)
 
-  if not (arg.no_start.lower() == "true" or arg.no_start.lower() == "false"):
-    argv_parser.error("-ns/--no-start arg is invalid, it should be true/false.")
-    exit(1)
+    if not (arg.no_start.lower() == "true" or arg.no_start.lower() == "false"):
+        argv_parser.error("-ns/--no-start arg is invalid, it should be true/false.")
+        exit(1)
 
-  if arg.connection_handle1 is not None:
-    fake_name = "ConnectionHandle" + str(arg.connection_handle1)
-    update_audio_data("", "", PEER_ADDRESS, fake_name)
-    update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE,
-                      arg.connection_handle1)
-    if arg.no_start.lower() == "true":
-      update_audio_data(PEER_ADDRESS, fake_name, START, True)
-      update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP_STR_FORMAT, "Unknown")
-      update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
-      update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
-      update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
+    if arg.connection_handle1 is not None:
+        fake_name = "ConnectionHandle" + str(arg.connection_handle1)
+        update_audio_data("", "", PEER_ADDRESS, fake_name)
+        update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE, arg.connection_handle1)
+        if arg.no_start.lower() == "true":
+            update_audio_data(PEER_ADDRESS, fake_name, START, True)
+            update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP_STR_FORMAT, "Unknown")
+            update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
+            update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
+            update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
 
-  if arg.connection_handle2 is not None:
-    fake_name = "ConnectionHandle" + str(arg.connection_handle2)
-    update_audio_data("", "", PEER_ADDRESS, fake_name)
-    update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE,
-                      arg.connection_handle2)
-    if arg.no_start.lower() == "true":
-      update_audio_data(PEER_ADDRESS, fake_name, START, True)
-      update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP_STR_FORMAT, "Unknown")
-      update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
-      update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
-      update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
+    if arg.connection_handle2 is not None:
+        fake_name = "ConnectionHandle" + str(arg.connection_handle2)
+        update_audio_data("", "", PEER_ADDRESS, fake_name)
+        update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE, arg.connection_handle2)
+        if arg.no_start.lower() == "true":
+            update_audio_data(PEER_ADDRESS, fake_name, START, True)
+            update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP_STR_FORMAT, "Unknown")
+            update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
+            update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
+            update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
 
-  if arg.audio_control_attr_handle is not None:
-    global force_audio_control_attr_handle
-    force_audio_control_attr_handle = arg.audio_control_attr_handle
+    if arg.audio_control_attr_handle is not None:
+        global force_audio_control_attr_handle
+        force_audio_control_attr_handle = arg.audio_control_attr_handle
 
-  global full_debug
-  global simple_debug
-  if arg.full_debug.lower() == "true":
-    full_debug = True
-    simple_debug = True
-  elif arg.simple_debug.lower() == "true":
-    simple_debug = True
+    global full_debug
+    global simple_debug
+    if arg.full_debug.lower() == "true":
+        full_debug = True
+        simple_debug = True
+    elif arg.simple_debug.lower() == "true":
+        simple_debug = True
 
-  if os.path.isfile(arg.BTSNOOP):
-    return arg.BTSNOOP
-  else:
-    argv_parser.error("BTSNOOP file not found: %s" % arg.BTSNOOP)
-    exit(1)
+    if os.path.isfile(arg.BTSNOOP):
+        return arg.BTSNOOP
+    else:
+        argv_parser.error("BTSNOOP file not found: %s" % arg.BTSNOOP)
+        exit(1)
 
 
 def main():
-  btsnoop_file_name = set_config()
+    btsnoop_file_name = set_config()
 
-  with open(btsnoop_file_name, "rb") as btsnoop_file:
-    identification = btsnoop_file.read(8)
-    if identification != "btsnoop\0":
-      sys.stderr.write(
-          "Check identification fail. It is not correct btsnoop file.")
-      exit(1)
+    with open(btsnoop_file_name, "rb") as btsnoop_file:
+        identification = btsnoop_file.read(8)
+        if identification != "btsnoop\0":
+            sys.stderr.write("Check identification fail. It is not correct btsnoop file.")
+            exit(1)
 
-    ver, data_link = struct.unpack(">II", btsnoop_file.read(4 + 4))
-    if (ver != 1) or (data_link != 1002):
-      sys.stderr.write(
-          "Check ver or dataLink fail. It is not correct btsnoop file.")
-      exit(1)
+        ver, data_link = struct.unpack(">II", btsnoop_file.read(4 + 4))
+        if (ver != 1) or (data_link != 1002):
+            sys.stderr.write("Check ver or dataLink fail. It is not correct btsnoop file.")
+            exit(1)
 
-    while True:
-      if not parse_packet(btsnoop_file):
-        break
+        while True:
+            if not parse_packet(btsnoop_file):
+                break
 
-    for i in audio_data:
-      if audio_data[i].get(START, False):
-        dump_audio_data(audio_data[i])
+        for i in audio_data:
+            if audio_data[i].get(START, False):
+                dump_audio_data(audio_data[i])
 
 
 if __name__ == "__main__":
-  main()
+    main()
diff --git a/tools/scripts/dump_metrics_ascii.py b/tools/scripts/dump_metrics_ascii.py
index c24d299..0adb278 100755
--- a/tools/scripts/dump_metrics_ascii.py
+++ b/tools/scripts/dump_metrics_ascii.py
@@ -23,6 +23,7 @@
 import google.protobuf.text_format as text_format
 from importlib import import_module
 
+
 def compile_proto(proto_path, output_dir):
     """Invoke Protocol Compiler to generate python from given source .proto."""
     # Find compiler path
@@ -32,10 +33,8 @@
     if not protoc:
         protoc = find_executable('protoc')
     if not protoc:
-        logging.error(
-            "Cannot find Protobuf compiler (>=3.0.0), please install"
-            "protobuf-compiler package. Prefer copying from <top>/prebuilts/tools"
-        )
+        logging.error("Cannot find Protobuf compiler (>=3.0.0), please install"
+                      "protobuf-compiler package. Prefer copying from <top>/prebuilts/tools")
         logging.error("    prebuilts/tools/linux-x86_64/protoc/bin/protoc")
         logging.error("If prebuilts are not available, use apt-get:")
         logging.error("    sudo apt-get install protobuf-compiler")
@@ -48,16 +47,12 @@
     if not os.path.exists(output_dir):
         os.mkdirs(output_dir)
     elif not os.path.isdir(output_dir):
-        logging.error("Output path is not a valid directory: %s" %
-                      (output_dir))
+        logging.error("Output path is not a valid directory: %s" % (output_dir))
         return None
     input_dir = os.path.dirname(proto_path)
     output_filename = os.path.basename(proto_path).replace('.proto', '_pb2.py')
     output_path = os.path.join(output_dir, output_filename)
-    protoc_command = [
-        protoc, '-I=%s' % (input_dir), '--python_out=%s' % (output_dir),
-        proto_path
-    ]
+    protoc_command = [protoc, '-I=%s' % (input_dir), '--python_out=%s' % (output_dir), proto_path]
     if subprocess.call(protoc_command, stderr=subprocess.STDOUT) != 0:
         logging.error("Fail to compile proto")
         return None
@@ -81,8 +76,7 @@
     try:
         output_module = import_module(output_module_name)
     except ImportError:
-        logging.error("Cannot import generated py-proto %s" %
-                      (output_module_name))
+        logging.error("Cannot import generated py-proto %s" % (output_module_name))
     return output_module
 
 
@@ -94,26 +88,31 @@
     """
     return text_format.MessageToString(binary_proto_msg)
 
+
 def dump_metrics():
     os.system('adb wait-for-device')
-    p = subprocess.Popen("adb shell dumpsys bluetooth_manager --proto-bin",
-        shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+    p = subprocess.Popen(
+        "adb shell dumpsys bluetooth_manager --proto-bin",
+        shell=True,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
         stdin=subprocess.PIPE)
     return p.communicate()
 
+
 def get_bluetooth_metrics(proto_native_str_64, bluetooth_proto_module):
     bluetooth_log = bluetooth_proto_module.BluetoothLog()
     proto_native_str = base64.b64decode(proto_native_str_64)
     bluetooth_log.MergeFromString(proto_native_str)
     return bluetooth_log
 
+
 def main():
     root = logging.getLogger()
     root.setLevel(logging.DEBUG)
     log_handler = logging.StreamHandler(sys.stderr)
     log_handler.setLevel(logging.DEBUG)
-    formatter = logging.Formatter(
-        "%(asctime)s %(levelname)s %(message)s")
+    formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
     log_handler.setFormatter(formatter)
     root.addHandler(log_handler)
     if len(sys.argv) < 2:
@@ -124,8 +123,7 @@
         logging.info("Usage: " + sys.argv[0] + " <path_to_metric_proto>")
         logging.info("Requires Protobuf compiler, protoc, version >=3.0.0")
         sys.exit(0)
-    bluetooth_proto_module = compile_import_proto(tempfile.gettempdir(),
-        sys.argv[1])
+    bluetooth_proto_module = compile_import_proto(tempfile.gettempdir(), sys.argv[1])
     if not bluetooth_proto_module:
         logging.error("Cannot compile " + sys.argv[1])
         sys.exit(1)
@@ -136,5 +134,6 @@
     bluetooth_log_ascii = parse_proto_to_ascii(bluetooth_log)
     print(bluetooth_log_ascii)
 
+
 if __name__ == "__main__":
     main()
diff --git a/tools/scripts/yapf_checker.py b/tools/scripts/yapf_checker.py
new file mode 100755
index 0000000..62a753e
--- /dev/null
+++ b/tools/scripts/yapf_checker.py
@@ -0,0 +1,76 @@
+#!/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 logging
+import os
+import subprocess
+import sys
+
+PYTHONPATH_KEY = 'PYTHONPATH'
+COMMIT_ID_ENV_KEY = 'PREUPLOAD_COMMIT'
+ANDROID_BUILD_TOP_KEY = 'ANDROID_BUILD_TOP'
+DEFAULT_YAPF_DIR = 'external/yapf'
+GIT_COMMAND = ['git', 'diff-tree', '--no-commit-id', '--name-only', '--diff-filter=d']
+
+
+def main():
+    """
+    A Python commit formatter using YAPF
+
+    Caveat: if you modify a file, the entire file will be formatted, instead of
+            the diff
+    :return:
+    """
+    if COMMIT_ID_ENV_KEY not in os.environ:
+        logging.error('Missing PREUPLOAD_COMMIT in environment.')
+        exit(1)
+
+    if ANDROID_BUILD_TOP_KEY not in os.environ:
+        logging.error('Missing ANDROID_BUILD_TOP in environment.')
+        exit(1)
+
+    # Gather changed Python files
+    commit_id = os.environ[COMMIT_ID_ENV_KEY]
+    full_git_command = GIT_COMMAND + ['-r', commit_id]
+    files = subprocess.check_output(full_git_command).decode('utf-8').splitlines()
+    full_files = [os.path.abspath(f) for f in files if f.endswith('.py')]
+    if not full_files:
+        return
+
+    # Find yapf in Android code tree
+    yapf_dir = os.path.join(os.environ[ANDROID_BUILD_TOP_KEY], DEFAULT_YAPF_DIR)
+    yapf_binary = os.path.join(yapf_dir, 'yapf')
+
+    # Run YAPF
+    full_yapf_command = ["%s=$%s:%s" % (PYTHONPATH_KEY, PYTHONPATH_KEY, yapf_dir), 'python3', yapf_binary, '-d', '-p'
+                        ] + full_files
+    environment = os.environ.copy()
+    environment[PYTHONPATH_KEY] = environment[PYTHONPATH_KEY] + ":" + yapf_dir
+    result = subprocess.run(full_yapf_command[1:], env=environment, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
+
+    if result.returncode != 0 or result.stdout:
+        logging.error(result.stdout.decode('utf-8').strip())
+        logging.error('INVALID FORMATTING, return code %d', result.returncode)
+        logging.error('To re-run the format command:\n\n' '    %s\n' % ' '.join(full_yapf_command))
+        yapf_inplace_format = ' '.join(
+            ["%s=$%s:%s" % (PYTHONPATH_KEY, PYTHONPATH_KEY, yapf_dir), 'python3', yapf_binary, '-p', '-i'] + full_files)
+        logging.error('If this is a legitimate format error, please run:\n\n' '    %s\n' % yapf_inplace_format)
+        logging.error('CAVEAT: Currently, this format the entire Python file if you modify even part of it')
+        exit(1)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/types/Android.bp b/types/Android.bp
index 444b6ff..bdd319d 100644
--- a/types/Android.bp
+++ b/types/Android.bp
@@ -4,6 +4,16 @@
     export_include_dirs: ["./"],
     vendor_available: true,
     host_supported: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+        "com.android.bluetooth.updatable"
+    ],
+    // As part of updatable mainline modules(media, swcodec), it should support at least 29(Q)
+    min_sdk_version: "29",
 }
 
 cc_library_static {
diff --git a/types/raw_address.h b/types/raw_address.h
index 49443a8..b0035c9 100644
--- a/types/raw_address.h
+++ b/types/raw_address.h
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include <cstring>
 #include <string>
 
 /** Bluetooth Address */
@@ -64,3 +65,14 @@
   os << a.ToString();
   return os;
 }
+
+template <>
+struct std::hash<RawAddress> {
+  std::size_t operator()(const RawAddress& val) const {
+    static_assert(sizeof(uint64_t) >= RawAddress::kLength);
+    uint64_t int_addr = 0;
+    memcpy(reinterpret_cast<uint8_t*>(&int_addr), val.address,
+           RawAddress::kLength);
+    return std::hash<uint64_t>{}(int_addr);
+  }
+};
diff --git a/udrv/ulinux/uipc.cc b/udrv/ulinux/uipc.cc
index 24fa711..71f46f6 100644
--- a/udrv/ulinux/uipc.cc
+++ b/udrv/ulinux/uipc.cc
@@ -485,7 +485,7 @@
   uipc.running = 1;
 
   if (pthread_create(&uipc.tid, (const pthread_attr_t*)NULL, uipc_read_task,
-                     &uipc) < 0) {
+                     &uipc) != 0) {
     BTIF_TRACE_ERROR("uipc_thread_create pthread_create failed:%d", errno);
     return -1;
   }
diff --git a/utils/src/bt_utils.cc b/utils/src/bt_utils.cc
index fda47f6..0ee53b2 100644
--- a/utils/src/bt_utils.cc
+++ b/utils/src/bt_utils.cc
@@ -135,8 +135,7 @@
   }
 
   if (rc) {
-    LOG_WARN(LOG_TAG, "failed to change sched policy, tid %d, err: %d", tid,
-             errno);
+    LOG_WARN("failed to change sched policy, tid %d, err: %d", tid, errno);
   }
 
   // make A2DP threads use RT scheduling policy since they are part of the
@@ -147,8 +146,7 @@
 
     const int rc = sched_setscheduler(tid, SCHED_FIFO, &rt_params);
     if (rc != 0) {
-      LOG_ERROR(LOG_TAG,
-                "%s unable to set SCHED_FIFO priority %d for tid %d, error %s",
+      LOG_ERROR("%s unable to set SCHED_FIFO priority %d for tid %d, error %s",
                 __func__, A2DP_RT_PRIORITY, tid, strerror(errno));
     }
   }
diff --git a/vendor_libs/test_vendor_lib/Android.bp b/vendor_libs/test_vendor_lib/Android.bp
index 16662c2..7dc88cf 100644
--- a/vendor_libs/test_vendor_lib/Android.bp
+++ b/vendor_libs/test_vendor_lib/Android.bp
@@ -2,13 +2,17 @@
 // ========================================================
 cc_library_static {
     name: "libbt-rootcanal",
-    defaults: ["libchrome_support_defaults"],
+    defaults: [
+        "gd_defaults",
+        "gd_clang_tidy",
+    ],
     host_supported: true,
     proprietary: true,
     srcs: [
         "model/controller/acl_connection.cc",
         "model/controller/acl_connection_handler.cc",
         "model/controller/dual_mode_controller.cc",
+        "model/controller/le_advertiser.cc",
         "model/controller/link_layer_controller.cc",
         "model/controller/security_manager.cc",
         "model/devices/beacon.cc",
@@ -28,6 +32,7 @@
         "model/devices/loopback.cc",
         "model/devices/polled_socket.cc",
         "model/devices/remote_loopback_device.cc",
+        "model/devices/scripted_beacon.cc",
         "model/devices/sniffer.cc",
         "model/setup/async_manager.cc",
         "model/setup/device_boutique.cc",
@@ -59,13 +64,26 @@
     ],
     shared_libs: [
         "libbase",
+        "libchrome",
         "liblog",
     ],
     static_libs: [
         "libbt-rootcanal-types",
+        "libscriptedbeaconpayload-protos-lite",
     ],
 }
 
+cc_library_static {
+    name: "libscriptedbeaconpayload-protos-lite",
+    host_supported: true,
+    proprietary: true,
+    proto: {
+        export_proto_headers: true,
+        type: "lite",
+    },
+    srcs: ["model/devices/scripted_beacon_ble_payload.proto"],
+}
+
 // test-vendor unit tests for host
 // ========================================================
 cc_test_host {
@@ -104,10 +122,11 @@
 
 // Linux RootCanal Executable
 // ========================================================
-cc_test_host {
+cc_binary_host {
     name: "root-canal",
     defaults: [
         "libchrome_support_defaults",
+        "gd_clang_tidy",
     ],
     srcs: [
         "desktop/root_canal_main.cc",
@@ -129,11 +148,19 @@
     ],
     shared_libs: [
         "liblog",
+        "libbacktrace",
     ],
     static_libs: [
         "libbt-rootcanal-types",
+        "libprotobuf-cpp-lite",
+        "libscriptedbeaconpayload-protos-lite",
         "libbt-rootcanal",
+        "breakpad_client",
     ],
+    sanitize: {
+        address: true,
+        all_undefined: true,
+    },
 }
 
 genrule {
diff --git a/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc b/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc
index 95bbfd3..ff35b09 100644
--- a/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc
+++ b/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc
@@ -18,6 +18,11 @@
 
 #include <future>
 
+#include <client/linux/handler/exception_handler.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/backtrace_constants.h>
+
 #include "os/log.h"
 
 using ::android::bluetooth::root_canal::TestEnvironment;
@@ -26,7 +31,45 @@
 constexpr uint16_t kHciServerPort = 6402;
 constexpr uint16_t kLinkServerPort = 6403;
 
+bool crash_callback(const void* crash_context, size_t crash_context_size,
+                    __attribute__((unused)) void* context) {
+  pid_t tid = BACKTRACE_CURRENT_THREAD;
+  if (crash_context_size >=
+      sizeof(google_breakpad::ExceptionHandler::CrashContext)) {
+    auto* ctx =
+        static_cast<const google_breakpad::ExceptionHandler::CrashContext*>(
+            crash_context);
+    tid = ctx->tid;
+    int signal_number = ctx->siginfo.si_signo;
+    LOG_ERROR("Process crashed, signal: %s[%d], tid: %d",
+              strsignal(signal_number), signal_number, ctx->tid);
+  } else {
+    LOG_ERROR("Process crashed, signal: unknown, tid: unknown");
+  }
+  std::unique_ptr<Backtrace> backtrace(
+      Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+  if (backtrace == nullptr) {
+    LOG_ERROR("Failed to create backtrace object");
+    return false;
+  }
+  if (!backtrace->Unwind(0)) {
+    LOG_ERROR("backtrace->Unwind failed");
+    return false;
+  }
+  LOG_ERROR("Backtrace:");
+  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+    LOG_ERROR("%s", backtrace->FormatFrameData(i).c_str());
+  }
+  return true;
+}
+
 int main(int argc, char** argv) {
+  google_breakpad::MinidumpDescriptor descriptor(
+      google_breakpad::MinidumpDescriptor::kMicrodumpOnConsole);
+  google_breakpad::ExceptionHandler eh(descriptor, nullptr, nullptr, nullptr,
+                                       true, -1);
+  eh.set_crash_handler(crash_callback);
+
   LOG_INFO("main");
   uint16_t test_port = kTestPort;
   uint16_t hci_server_port = kHciServerPort;
diff --git a/vendor_libs/test_vendor_lib/desktop/test_environment.cc b/vendor_libs/test_vendor_lib/desktop/test_environment.cc
index 1ca3b8c..6b84b86 100644
--- a/vendor_libs/test_vendor_lib/desktop/test_environment.cc
+++ b/vendor_libs/test_vendor_lib/desktop/test_environment.cc
@@ -59,6 +59,7 @@
 
 void TestEnvironment::close() {
   LOG_INFO("%s", __func__);
+  test_model_.Reset();
 }
 
 void TestEnvironment::SetUpHciServer(const std::function<void(int)>& connection_callback) {
@@ -126,7 +127,7 @@
     return -1;
   }
 
-  struct sockaddr_in serv_addr;
+  struct sockaddr_in serv_addr {};
   memset((void*)&serv_addr, 0, sizeof(serv_addr));
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
@@ -147,14 +148,14 @@
 
 void TestEnvironment::SetUpTestChannel() {
   int socket_fd = test_channel_transport_.SetUp(test_port_);
+  test_channel_.RegisterSendResponse([](const std::string& response) {
+    LOG_INFO("No test channel: %s", response.c_str());
+  });
   test_channel_.AddPhy({"BR_EDR"});
   test_channel_.AddPhy({"LOW_ENERGY"});
-  test_channel_.SetTimerPeriod({"10"});
+  test_channel_.SetTimerPeriod({"5"});
   test_channel_.StartTimer({});
 
-  test_channel_.RegisterSendResponse(
-      [](const std::string& response) { LOG_INFO("No test channel: %s", response.c_str()); });
-
   if (socket_fd == -1) {
     LOG_ERROR("Test channel SetUp(%d) failed.", test_port_);
     return;
diff --git a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
index 8b0089f..be14ea5 100644
--- a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
@@ -35,12 +35,15 @@
 constexpr char DualModeController::kControllerPropertiesFile[];
 constexpr uint16_t DualModeController::kSecurityManagerNumKeys;
 constexpr uint16_t kNumCommandPackets = 0x01;
+constexpr uint16_t kLeMaximumAdvertisingDataLength = 256;
+constexpr uint16_t kLeMaximumDataLength = 64;
+constexpr uint16_t kLeMaximumDataTime = 0x148;
 
 // Device methods.
 void DualModeController::Initialize(const std::vector<std::string>& args) {
   if (args.size() < 2) return;
 
-  Address addr;
+  Address addr{};
   if (Address::FromString(args[1], addr)) {
     properties_.SetAddress(addr);
   } else {
@@ -78,7 +81,7 @@
     : Device(properties_filename), security_manager_(num_keys) {
   loopback_mode_ = LoopbackMode::NO_LOOPBACK;
 
-  Address public_address;
+  Address public_address{};
   ASSERT(Address::FromString("3C:5A:B4:04:05:06", public_address));
   properties_.SetAddress(public_address);
 
@@ -104,7 +107,7 @@
               ReadLocalSupportedCommands);
   SET_HANDLER(OpCode::READ_LOCAL_SUPPORTED_FEATURES,
               ReadLocalSupportedFeatures);
-  SET_HANDLER(OpCode::READ_LOCAL_SUPPORTED_CODECS, ReadLocalSupportedCodecs);
+  SET_HANDLER(OpCode::READ_LOCAL_SUPPORTED_CODECS_V1, ReadLocalSupportedCodecs);
   SET_HANDLER(OpCode::READ_LOCAL_EXTENDED_FEATURES, ReadLocalExtendedFeatures);
   SET_HANDLER(OpCode::READ_REMOTE_EXTENDED_FEATURES,
               ReadRemoteExtendedFeatures);
@@ -119,14 +122,19 @@
               UserConfirmationRequestNegativeReply);
   SET_HANDLER(OpCode::IO_CAPABILITY_REQUEST_NEGATIVE_REPLY,
               IoCapabilityRequestNegativeReply);
+  SET_HANDLER(OpCode::READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL,
+              ReadInquiryResponseTransmitPowerLevel);
   SET_HANDLER(OpCode::WRITE_SIMPLE_PAIRING_MODE, WriteSimplePairingMode);
   SET_HANDLER(OpCode::WRITE_LE_HOST_SUPPORT, WriteLeHostSupport);
   SET_HANDLER(OpCode::WRITE_SECURE_CONNECTIONS_HOST_SUPPORT,
               WriteSecureConnectionsHostSupport);
   SET_HANDLER(OpCode::SET_EVENT_MASK, SetEventMask);
+  SET_HANDLER(OpCode::READ_INQUIRY_MODE, ReadInquiryMode);
   SET_HANDLER(OpCode::WRITE_INQUIRY_MODE, WriteInquiryMode);
+  SET_HANDLER(OpCode::READ_PAGE_SCAN_TYPE, ReadPageScanType);
   SET_HANDLER(OpCode::WRITE_PAGE_SCAN_TYPE, WritePageScanType);
   SET_HANDLER(OpCode::WRITE_INQUIRY_SCAN_TYPE, WriteInquiryScanType);
+  SET_HANDLER(OpCode::READ_INQUIRY_SCAN_TYPE, ReadInquiryScanType);
   SET_HANDLER(OpCode::AUTHENTICATION_REQUESTED, AuthenticationRequested);
   SET_HANDLER(OpCode::SET_CONNECTION_ENCRYPTION, SetConnectionEncryption);
   SET_HANDLER(OpCode::CHANGE_CONNECTION_LINK_KEY, ChangeConnectionLinkKey);
@@ -134,6 +142,7 @@
   SET_HANDLER(OpCode::WRITE_AUTHENTICATION_ENABLE, WriteAuthenticationEnable);
   SET_HANDLER(OpCode::READ_AUTHENTICATION_ENABLE, ReadAuthenticationEnable);
   SET_HANDLER(OpCode::WRITE_CLASS_OF_DEVICE, WriteClassOfDevice);
+  SET_HANDLER(OpCode::READ_PAGE_TIMEOUT, ReadPageTimeout);
   SET_HANDLER(OpCode::WRITE_PAGE_TIMEOUT, WritePageTimeout);
   SET_HANDLER(OpCode::WRITE_LINK_SUPERVISION_TIMEOUT,
               WriteLinkSupervisionTimeout);
@@ -141,6 +150,8 @@
   SET_HANDLER(OpCode::SNIFF_MODE, SniffMode);
   SET_HANDLER(OpCode::EXIT_SNIFF_MODE, ExitSniffMode);
   SET_HANDLER(OpCode::QOS_SETUP, QosSetup);
+  SET_HANDLER(OpCode::READ_DEFAULT_LINK_POLICY_SETTINGS,
+              ReadDefaultLinkPolicySettings);
   SET_HANDLER(OpCode::WRITE_DEFAULT_LINK_POLICY_SETTINGS,
               WriteDefaultLinkPolicySettings);
   SET_HANDLER(OpCode::FLOW_SPECIFICATION, FlowSpecification);
@@ -153,8 +164,14 @@
               WriteExtendedInquiryResponse);
   SET_HANDLER(OpCode::REFRESH_ENCRYPTION_KEY, RefreshEncryptionKey);
   SET_HANDLER(OpCode::WRITE_VOICE_SETTING, WriteVoiceSetting);
+  SET_HANDLER(OpCode::READ_NUMBER_OF_SUPPORTED_IAC, ReadNumberOfSupportedIac);
+  SET_HANDLER(OpCode::READ_CURRENT_IAC_LAP, ReadCurrentIacLap);
   SET_HANDLER(OpCode::WRITE_CURRENT_IAC_LAP, WriteCurrentIacLap);
+  SET_HANDLER(OpCode::READ_PAGE_SCAN_ACTIVITY, ReadPageScanActivity);
+  SET_HANDLER(OpCode::WRITE_PAGE_SCAN_ACTIVITY, WritePageScanActivity);
+  SET_HANDLER(OpCode::READ_INQUIRY_SCAN_ACTIVITY, ReadInquiryScanActivity);
   SET_HANDLER(OpCode::WRITE_INQUIRY_SCAN_ACTIVITY, WriteInquiryScanActivity);
+  SET_HANDLER(OpCode::READ_SCAN_ENABLE, ReadScanEnable);
   SET_HANDLER(OpCode::WRITE_SCAN_ENABLE, WriteScanEnable);
   SET_HANDLER(OpCode::SET_EVENT_FILTER, SetEventFilter);
   SET_HANDLER(OpCode::INQUIRY, Inquiry);
@@ -167,7 +184,7 @@
   SET_HANDLER(OpCode::DELETE_STORED_LINK_KEY, DeleteStoredLinkKey);
   SET_HANDLER(OpCode::REMOTE_NAME_REQUEST, RemoteNameRequest);
   SET_HANDLER(OpCode::LE_SET_EVENT_MASK, LeSetEventMask);
-  SET_HANDLER(OpCode::LE_READ_BUFFER_SIZE, LeReadBufferSize);
+  SET_HANDLER(OpCode::LE_READ_BUFFER_SIZE_V1, LeReadBufferSize);
   SET_HANDLER(OpCode::LE_READ_LOCAL_SUPPORTED_FEATURES,
               LeReadLocalSupportedFeatures);
   SET_HANDLER(OpCode::LE_SET_RANDOM_ADDRESS, LeSetRandomAddress);
@@ -182,18 +199,33 @@
   SET_HANDLER(OpCode::CREATE_CONNECTION, CreateConnection);
   SET_HANDLER(OpCode::DISCONNECT, Disconnect);
   SET_HANDLER(OpCode::LE_CREATE_CONNECTION_CANCEL, LeConnectionCancel);
-  SET_HANDLER(OpCode::LE_READ_WHITE_LIST_SIZE, LeReadWhiteListSize);
-  SET_HANDLER(OpCode::LE_CLEAR_WHITE_LIST, LeClearWhiteList);
-  SET_HANDLER(OpCode::LE_ADD_DEVICE_TO_WHITE_LIST, LeAddDeviceToWhiteList);
-  SET_HANDLER(OpCode::LE_REMOVE_DEVICE_FROM_WHITE_LIST,
-              LeRemoveDeviceFromWhiteList);
+  SET_HANDLER(OpCode::LE_READ_CONNECT_LIST_SIZE, LeReadConnectListSize);
+  SET_HANDLER(OpCode::LE_CLEAR_CONNECT_LIST, LeClearConnectList);
+  SET_HANDLER(OpCode::LE_ADD_DEVICE_TO_CONNECT_LIST, LeAddDeviceToConnectList);
+  SET_HANDLER(OpCode::LE_REMOVE_DEVICE_FROM_CONNECT_LIST,
+              LeRemoveDeviceFromConnectList);
   SET_HANDLER(OpCode::LE_RAND, LeRand);
   SET_HANDLER(OpCode::LE_READ_SUPPORTED_STATES, LeReadSupportedStates);
   SET_HANDLER(OpCode::LE_GET_VENDOR_CAPABILITIES, LeVendorCap);
   SET_HANDLER(OpCode::LE_MULTI_ADVT, LeVendorMultiAdv);
   SET_HANDLER(OpCode::LE_ADV_FILTER, LeAdvertisingFilter);
   SET_HANDLER(OpCode::LE_ENERGY_INFO, LeEnergyInfo);
-  SET_HANDLER(OpCode::LE_EXTENDED_SCAN_PARAMS, LeExtendedScanParams);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS,
+              LeSetExtendedAdvertisingRandomAddress);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS,
+              LeSetExtendedAdvertisingParameters);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_DATA,
+              LeSetExtendedAdvertisingData);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE,
+              LeSetExtendedAdvertisingScanResponse);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_ENABLE,
+              LeSetExtendedAdvertisingEnable);
+  SET_HANDLER(OpCode::LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH,
+              LeReadMaximumAdvertisingDataLength);
+  SET_HANDLER(OpCode::LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS,
+              LeReadNumberOfSupportedAdvertisingSets);
+  SET_HANDLER(OpCode::LE_REMOVE_ADVERTISING_SET, LeRemoveAdvertisingSet);
+  SET_HANDLER(OpCode::LE_CLEAR_ADVERTISING_SETS, LeClearAdvertisingSets);
   SET_HANDLER(OpCode::LE_READ_REMOTE_FEATURES, LeReadRemoteFeatures);
   SET_HANDLER(OpCode::READ_REMOTE_VERSION_INFORMATION,
               ReadRemoteVersionInformation);
@@ -204,6 +236,13 @@
   SET_HANDLER(OpCode::LE_REMOVE_DEVICE_FROM_RESOLVING_LIST,
               LeRemoveDeviceFromResolvingList);
   SET_HANDLER(OpCode::LE_CLEAR_RESOLVING_LIST, LeClearResolvingList);
+  SET_HANDLER(OpCode::LE_READ_RESOLVING_LIST_SIZE, LeReadResolvingListSize);
+  SET_HANDLER(OpCode::LE_READ_MAXIMUM_DATA_LENGTH, LeReadMaximumDataLength);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS,
+              LeSetExtendedScanParameters);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_SCAN_ENABLE, LeSetExtendedScanEnable);
+  SET_HANDLER(OpCode::LE_EXTENDED_CREATE_CONNECTION,
+              LeExtendedCreateConnection);
   SET_HANDLER(OpCode::LE_SET_PRIVACY_MODE, LeSetPrivacyMode);
   // Testing Commands
   SET_HANDLER(OpCode::READ_LOOPBACK_MODE, ReadLoopbackMode);
@@ -413,7 +452,7 @@
 void DualModeController::ReadRemoteVersionInformation(
     CommandPacketView command) {
   auto command_view = gd_hci::ReadRemoteVersionInformationView::Create(
-      gd_hci::DiscoveryCommandView::Create(command));
+      gd_hci::ConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
   auto status = link_layer_controller_.SendCommandToRemoteByHandle(
@@ -438,7 +477,7 @@
   auto command_view = gd_hci::ReadLocalSupportedCommandsView::Create(command);
   ASSERT(command_view.IsValid());
 
-  std::array<uint8_t, 64> supported_commands;
+  std::array<uint8_t, 64> supported_commands{};
   supported_commands.fill(0x00);
   size_t len = properties_.GetSupportedCommands().size();
   if (len > 64) {
@@ -464,11 +503,13 @@
 }
 
 void DualModeController::ReadLocalSupportedCodecs(CommandPacketView command) {
-  auto command_view = gd_hci::ReadLocalSupportedCodecsView::Create(command);
+  auto command_view = gd_hci::ReadLocalSupportedCodecsV1View::Create(command);
   ASSERT(command_view.IsValid());
-  auto packet = bluetooth::hci::ReadLocalSupportedCodecsCompleteBuilder::Create(
-      kNumCommandPackets, ErrorCode::SUCCESS, properties_.GetSupportedCodecs(),
-      properties_.GetVendorSpecificCodecs());
+  auto packet =
+      bluetooth::hci::ReadLocalSupportedCodecsV1CompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS,
+          properties_.GetSupportedCodecs(),
+          properties_.GetVendorSpecificCodecs());
   send_event_(std::move(packet));
 }
 
@@ -487,7 +528,7 @@
 
 void DualModeController::ReadRemoteExtendedFeatures(CommandPacketView command) {
   auto command_view = gd_hci::ReadRemoteExtendedFeaturesView::Create(
-      gd_hci::DiscoveryCommandView::Create(command));
+      gd_hci::ConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
   auto status = link_layer_controller_.SendCommandToRemoteByHandle(
@@ -515,7 +556,7 @@
 void DualModeController::ReadRemoteSupportedFeatures(
     CommandPacketView command) {
   auto command_view = gd_hci::ReadRemoteSupportedFeaturesView::Create(
-      gd_hci::DiscoveryCommandView::Create(command));
+      gd_hci::ConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
   auto status = link_layer_controller_.SendCommandToRemoteByHandle(
@@ -681,6 +722,19 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::ReadInquiryResponseTransmitPowerLevel(
+    CommandPacketView command) {
+  auto command_view = gd_hci::ReadInquiryResponseTransmitPowerLevelView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  uint8_t tx_power = 20;  // maximum
+  auto packet =
+      bluetooth::hci::ReadInquiryResponseTransmitPowerLevelCompleteBuilder::
+          Create(kNumCommandPackets, ErrorCode::SUCCESS, tx_power);
+  send_event_(std::move(packet));
+}
+
 void DualModeController::WriteSimplePairingMode(CommandPacketView command) {
   auto command_view = gd_hci::WriteSimplePairingModeView::Create(
       gd_hci::SecurityCommandView::Create(command));
@@ -736,6 +790,16 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::ReadInquiryMode(CommandPacketView command) {
+  auto command_view = gd_hci::ReadInquiryModeView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  gd_hci::InquiryMode inquiry_mode = gd_hci::InquiryMode::STANDARD;
+  auto packet = bluetooth::hci::ReadInquiryModeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, inquiry_mode);
+  send_event_(std::move(packet));
+}
+
 void DualModeController::WriteInquiryMode(CommandPacketView command) {
   auto command_view = gd_hci::WriteInquiryModeView::Create(
       gd_hci::DiscoveryCommandView::Create(command));
@@ -747,6 +811,16 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::ReadPageScanType(CommandPacketView command) {
+  auto command_view = gd_hci::ReadPageScanTypeView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  gd_hci::PageScanType page_scan_type = gd_hci::PageScanType::STANDARD;
+  auto packet = bluetooth::hci::ReadPageScanTypeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, page_scan_type);
+  send_event_(std::move(packet));
+}
+
 void DualModeController::WritePageScanType(CommandPacketView command) {
   auto command_view = gd_hci::WritePageScanTypeView::Create(
       gd_hci::DiscoveryCommandView::Create(command));
@@ -756,6 +830,16 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::ReadInquiryScanType(CommandPacketView command) {
+  auto command_view = gd_hci::ReadInquiryScanTypeView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  gd_hci::InquiryScanType inquiry_scan_type = gd_hci::InquiryScanType::STANDARD;
+  auto packet = bluetooth::hci::ReadInquiryScanTypeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, inquiry_scan_type);
+  send_event_(std::move(packet));
+}
+
 void DualModeController::WriteInquiryScanType(CommandPacketView command) {
   auto command_view = gd_hci::WriteInquiryScanTypeView::Create(
       gd_hci::DiscoveryCommandView::Create(command));
@@ -852,6 +936,16 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::ReadPageTimeout(CommandPacketView command) {
+  auto command_view = gd_hci::ReadPageTimeoutView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t page_timeout = 0x2000;
+  auto packet = bluetooth::hci::ReadPageTimeoutCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, page_timeout);
+  send_event_(std::move(packet));
+}
+
 void DualModeController::WritePageTimeout(CommandPacketView command) {
   auto command_view = gd_hci::WritePageTimeoutView::Create(
       gd_hci::DiscoveryCommandView::Create(command));
@@ -929,14 +1023,28 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::ReadDefaultLinkPolicySettings(
+    CommandPacketView command) {
+  auto command_view = gd_hci::ReadDefaultLinkPolicySettingsView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t settings = link_layer_controller_.ReadDefaultLinkPolicySettings();
+  auto packet =
+      bluetooth::hci::ReadDefaultLinkPolicySettingsCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS, settings);
+  send_event_(std::move(packet));
+}
+
 void DualModeController::WriteDefaultLinkPolicySettings(
     CommandPacketView command) {
   auto command_view = gd_hci::WriteDefaultLinkPolicySettingsView::Create(
       gd_hci::ConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
+  ErrorCode status = link_layer_controller_.WriteDefaultLinkPolicySettings(
+      command_view.GetDefaultLinkPolicySettings());
   auto packet =
       bluetooth::hci::WriteDefaultLinkPolicySettingsCompleteBuilder::Create(
-          kNumCommandPackets, ErrorCode::SUCCESS);
+          kNumCommandPackets, status);
   send_event_(std::move(packet));
 }
 
@@ -984,7 +1092,7 @@
       gd_hci::ConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
-  uint16_t handle = command_view.GetHandle();
+  uint16_t handle = command_view.GetConnectionHandle();
   uint16_t timeout = command_view.GetLinkSupervisionTimeout();
 
   auto status =
@@ -999,7 +1107,7 @@
   auto command_view = gd_hci::ReadLocalNameView::Create(command);
   ASSERT(command_view.IsValid());
 
-  std::array<uint8_t, 248> local_name;
+  std::array<uint8_t, 248> local_name{};
   local_name.fill(0x00);
   size_t len = properties_.GetName().size();
   if (len > 247) {
@@ -1015,8 +1123,12 @@
 void DualModeController::WriteLocalName(CommandPacketView command) {
   auto command_view = gd_hci::WriteLocalNameView::Create(command);
   ASSERT(command_view.IsValid());
-  properties_.SetName(std::vector<uint8_t>(command_view.GetLocalName().begin(),
-                                           command_view.GetLocalName().end()));
+  const auto local_name = command_view.GetLocalName();
+  std::vector<uint8_t> name_vec(248);
+  for (size_t i = 0; i < 248; i++) {
+    name_vec[i] = local_name[i];
+  }
+  properties_.SetName(name_vec);
   auto packet = bluetooth::hci::WriteLocalNameCompleteBuilder::Create(
       kNumCommandPackets, ErrorCode::SUCCESS);
   send_event_(std::move(packet));
@@ -1058,6 +1170,27 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::ReadNumberOfSupportedIac(CommandPacketView command) {
+  auto command_view = gd_hci::ReadNumberOfSupportedIacView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint8_t num_support_iac = 0x1;
+  auto packet = bluetooth::hci::ReadNumberOfSupportedIacCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, num_support_iac);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadCurrentIacLap(CommandPacketView command) {
+  auto command_view = gd_hci::ReadCurrentIacLapView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  gd_hci::Lap lap;
+  lap.lap_ = 0x30;
+  auto packet = bluetooth::hci::ReadCurrentIacLapCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, {lap});
+  send_event_(std::move(packet));
+}
+
 void DualModeController::WriteCurrentIacLap(CommandPacketView command) {
   auto command_view = gd_hci::WriteCurrentIacLapView::Create(
       gd_hci::DiscoveryCommandView::Create(command));
@@ -1067,6 +1200,37 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::ReadPageScanActivity(CommandPacketView command) {
+  auto command_view = gd_hci::ReadPageScanActivityView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t interval = 0x1000;
+  uint16_t window = 0x0012;
+  auto packet = bluetooth::hci::ReadPageScanActivityCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, interval, window);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WritePageScanActivity(CommandPacketView command) {
+  auto command_view = gd_hci::WritePageScanActivityView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::WritePageScanActivityCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadInquiryScanActivity(CommandPacketView command) {
+  auto command_view = gd_hci::ReadInquiryScanActivityView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t interval = 0x1000;
+  uint16_t window = 0x0012;
+  auto packet = bluetooth::hci::ReadInquiryScanActivityCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, interval, window);
+  send_event_(std::move(packet));
+}
+
 void DualModeController::WriteInquiryScanActivity(CommandPacketView command) {
   auto command_view = gd_hci::WriteInquiryScanActivityView::Create(
       gd_hci::DiscoveryCommandView::Create(command));
@@ -1076,6 +1240,15 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::ReadScanEnable(CommandPacketView command) {
+  auto command_view = gd_hci::ReadScanEnableView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::ReadScanEnableCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, gd_hci::ScanEnable::NO_SCANS);
+  send_event_(std::move(packet));
+}
+
 void DualModeController::WriteScanEnable(CommandPacketView command) {
   auto command_view = gd_hci::WriteScanEnableView::Create(
       gd_hci::DiscoveryCommandView::Create(command));
@@ -1227,14 +1400,14 @@
 }
 
 void DualModeController::LeReadBufferSize(CommandPacketView command) {
-  auto command_view = gd_hci::LeReadBufferSizeView::Create(command);
+  auto command_view = gd_hci::LeReadBufferSizeV1View::Create(command);
   ASSERT(command_view.IsValid());
 
   bluetooth::hci::LeBufferSize le_buffer_size;
   le_buffer_size.le_data_packet_length_ = properties_.GetLeDataPacketLength();
   le_buffer_size.total_num_le_packets_ = properties_.GetTotalNumLeDataPackets();
 
-  auto packet = bluetooth::hci::LeReadBufferSizeCompleteBuilder::Create(
+  auto packet = bluetooth::hci::LeReadBufferSizeV1CompleteBuilder::Create(
       kNumCommandPackets, ErrorCode::SUCCESS, le_buffer_size);
   send_event_(std::move(packet));
 }
@@ -1264,12 +1437,18 @@
   auto command_view = gd_hci::LeSetAdvertisingParametersView::Create(
       gd_hci::LeAdvertisingCommandView::Create(command));
   ASSERT(command_view.IsValid());
+  auto peer_address = command_view.GetPeerAddress();
+  auto type = command_view.GetType();
+  if (type != bluetooth::hci::AdvertisingType::ADV_DIRECT_IND &&
+      type != bluetooth::hci::AdvertisingType::ADV_DIRECT_IND_LOW) {
+    peer_address = Address::kEmpty;
+  }
   properties_.SetLeAdvertisingParameters(
       command_view.GetIntervalMin(), command_view.GetIntervalMax(),
-      static_cast<uint8_t>(command_view.GetType()),
+      static_cast<uint8_t>(type),
       static_cast<uint8_t>(command_view.GetOwnAddressType()),
-      static_cast<uint8_t>(command_view.GetPeerAddressType()),
-      command_view.GetPeerAddress(), command_view.GetChannelMap(),
+      static_cast<uint8_t>(command_view.GetPeerAddressType()), peer_address,
+      command_view.GetChannelMap(),
       static_cast<uint8_t>(command_view.GetFilterPolicy()));
 
   auto packet =
@@ -1282,8 +1461,9 @@
   auto command_view = gd_hci::LeSetAdvertisingDataView::Create(
       gd_hci::LeAdvertisingCommandView::Create(command));
   auto payload = command.GetPayload();
-  std::vector<uint8_t> payload_bytes{payload.begin() + 1,
-                                     payload.begin() + *payload.begin()};
+  auto data_size = *payload.begin();
+  auto first_data = payload.begin() + 1;
+  std::vector<uint8_t> payload_bytes{first_data, first_data + data_size};
   ASSERT_LOG(command_view.IsValid(), "%s command.size() = %zu",
              gd_hci::OpCodeText(command.GetOpCode()).c_str(), command.size());
   ASSERT(command_view.GetPayload().size() == 32);
@@ -1311,9 +1491,8 @@
   ASSERT(command_view.IsValid());
   auto status = link_layer_controller_.SetLeAdvertisingEnable(
       command_view.GetAdvertisingEnable() == gd_hci::Enable::ENABLED);
-  auto packet = bluetooth::hci::LeSetAdvertisingEnableCompleteBuilder::Create(
-      kNumCommandPackets, status);
-  send_event_(std::move(packet));
+  send_event_(bluetooth::hci::LeSetAdvertisingEnableCompleteBuilder::Create(
+      kNumCommandPackets, status));
 }
 
 void DualModeController::LeSetScanParameters(CommandPacketView command) {
@@ -1337,8 +1516,11 @@
   auto command_view = gd_hci::LeSetScanEnableView::Create(
       gd_hci::LeScanningCommandView::Create(command));
   ASSERT(command_view.IsValid());
-  link_layer_controller_.SetLeScanEnable(command_view.GetLeScanEnable() ==
-                                         gd_hci::Enable::ENABLED);
+  if (command_view.GetLeScanEnable() == gd_hci::Enable::ENABLED) {
+    link_layer_controller_.SetLeScanEnable(gd_hci::OpCode::LE_SET_SCAN_ENABLE);
+  } else {
+    link_layer_controller_.SetLeScanEnable(gd_hci::OpCode::NONE);
+  }
   link_layer_controller_.SetLeFilterDuplicates(
       command_view.GetFilterDuplicates() == gd_hci::Enable::ENABLED);
   auto packet = bluetooth::hci::LeSetScanEnableCompleteBuilder::Create(
@@ -1356,7 +1538,7 @@
       static_cast<uint8_t>(command_view.GetInitiatorFilterPolicy());
   link_layer_controller_.SetLeInitiatorFilterPolicy(initiator_filter_policy);
 
-  if (initiator_filter_policy == 0) {  // White list not used
+  if (initiator_filter_policy == 0) {  // Connect list not used
     uint8_t peer_address_type =
         static_cast<uint8_t>(command_view.GetPeerAddressType());
     Address peer_address = command_view.GetPeerAddress();
@@ -1388,15 +1570,11 @@
   auto command_view = gd_hci::LeConnectionUpdateView::Create(
       gd_hci::LeConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
+  ErrorCode status = link_layer_controller_.LeConnectionUpdate(command_view);
 
   auto status_packet = bluetooth::hci::LeConnectionUpdateStatusBuilder::Create(
-      ErrorCode::CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR, kNumCommandPackets);
+      status, kNumCommandPackets);
   send_event_(std::move(status_packet));
-
-  auto complete_packet =
-      bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create(
-          ErrorCode::SUCCESS, 0x0002, 0x0006, 0x0000, 0x01f4);
-  send_event_(std::move(complete_packet));
 }
 
 void DualModeController::CreateConnection(CommandPacketView command) {
@@ -1443,8 +1621,8 @@
       gd_hci::LeConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
   link_layer_controller_.SetLeConnect(false);
-  auto packet = bluetooth::hci::LeCreateConnectionCancelStatusBuilder::Create(
-      ErrorCode::SUCCESS, kNumCommandPackets);
+  auto packet = bluetooth::hci::LeCreateConnectionCancelCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
   send_event_(std::move(packet));
   /* For testing Jakub's patch:  Figure out a neat way to call this without
      recompiling.  I'm thinking about a bad device. */
@@ -1454,55 +1632,57 @@
   */
 }
 
-void DualModeController::LeReadWhiteListSize(CommandPacketView command) {
-  auto command_view = gd_hci::LeReadWhiteListSizeView::Create(
+void DualModeController::LeReadConnectListSize(CommandPacketView command) {
+  auto command_view = gd_hci::LeReadConnectListSizeView::Create(
       gd_hci::LeConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
-  auto packet = bluetooth::hci::LeReadWhiteListSizeCompleteBuilder::Create(
-      kNumCommandPackets, ErrorCode::SUCCESS, properties_.GetLeWhiteListSize());
+  auto packet = bluetooth::hci::LeReadConnectListSizeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS,
+      properties_.GetLeConnectListSize());
   send_event_(std::move(packet));
 }
 
-void DualModeController::LeClearWhiteList(CommandPacketView command) {
-  auto command_view = gd_hci::LeClearWhiteListView::Create(
+void DualModeController::LeClearConnectList(CommandPacketView command) {
+  auto command_view = gd_hci::LeClearConnectListView::Create(
       gd_hci::LeConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
-  link_layer_controller_.LeWhiteListClear();
-  auto packet = bluetooth::hci::LeClearWhiteListCompleteBuilder::Create(
+  link_layer_controller_.LeConnectListClear();
+  auto packet = bluetooth::hci::LeClearConnectListCompleteBuilder::Create(
       kNumCommandPackets, ErrorCode::SUCCESS);
   send_event_(std::move(packet));
 }
 
-void DualModeController::LeAddDeviceToWhiteList(CommandPacketView command) {
-  auto command_view = gd_hci::LeAddDeviceToWhiteListView::Create(
+void DualModeController::LeAddDeviceToConnectList(CommandPacketView command) {
+  auto command_view = gd_hci::LeAddDeviceToConnectListView::Create(
       gd_hci::LeConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
-  if (link_layer_controller_.LeWhiteListFull()) {
-    auto packet = bluetooth::hci::LeAddDeviceToWhiteListCompleteBuilder::Create(
-        kNumCommandPackets, ErrorCode::MEMORY_CAPACITY_EXCEEDED);
+  if (link_layer_controller_.LeConnectListFull()) {
+    auto packet =
+        bluetooth::hci::LeAddDeviceToConnectListCompleteBuilder::Create(
+            kNumCommandPackets, ErrorCode::MEMORY_CAPACITY_EXCEEDED);
     send_event_(std::move(packet));
     return;
   }
   uint8_t addr_type = static_cast<uint8_t>(command_view.GetAddressType());
   Address address = command_view.GetAddress();
-  link_layer_controller_.LeWhiteListAddDevice(address, addr_type);
-  auto packet = bluetooth::hci::LeAddDeviceToWhiteListCompleteBuilder::Create(
+  link_layer_controller_.LeConnectListAddDevice(address, addr_type);
+  auto packet = bluetooth::hci::LeAddDeviceToConnectListCompleteBuilder::Create(
       kNumCommandPackets, ErrorCode::SUCCESS);
   send_event_(std::move(packet));
 }
 
-void DualModeController::LeRemoveDeviceFromWhiteList(
+void DualModeController::LeRemoveDeviceFromConnectList(
     CommandPacketView command) {
-  auto command_view = gd_hci::LeRemoveDeviceFromWhiteListView::Create(
+  auto command_view = gd_hci::LeRemoveDeviceFromConnectListView::Create(
       gd_hci::LeConnectionManagementCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
   uint8_t addr_type = static_cast<uint8_t>(command_view.GetAddressType());
   Address address = command_view.GetAddress();
-  link_layer_controller_.LeWhiteListRemoveDevice(address, addr_type);
+  link_layer_controller_.LeConnectListRemoveDevice(address, addr_type);
   auto packet =
-      bluetooth::hci::LeRemoveDeviceFromWhiteListCompleteBuilder::Create(
+      bluetooth::hci::LeRemoveDeviceFromConnectListCompleteBuilder::Create(
           kNumCommandPackets, ErrorCode::SUCCESS);
   send_event_(std::move(packet));
 }
@@ -1517,6 +1697,29 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::LeReadResolvingListSize(CommandPacketView command) {
+  auto command_view = gd_hci::LeReadResolvingListSizeView::Create(
+      gd_hci::LeSecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::LeReadResolvingListSizeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS,
+      properties_.GetLeResolvingListSize());
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeReadMaximumDataLength(CommandPacketView command) {
+  auto command_view = gd_hci::LeReadMaximumDataLengthView::Create(
+      gd_hci::LeSecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  bluetooth::hci::LeMaximumDataLength data_length;
+  data_length.supported_max_rx_octets_ = kLeMaximumDataLength;
+  data_length.supported_max_rx_time_ = kLeMaximumDataTime;
+  data_length.supported_max_tx_octets_ = kLeMaximumDataLength + 10;
+  data_length.supported_max_tx_time_ = kLeMaximumDataTime + 10;
+  send_event_(bluetooth::hci::LeReadMaximumDataLengthCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, data_length));
+}
+
 void DualModeController::LeAddDeviceToResolvingList(CommandPacketView command) {
   auto command_view = gd_hci::LeAddDeviceToResolvingListView::Create(
       gd_hci::LeSecurityCommandView::Create(command));
@@ -1561,6 +1764,83 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::LeSetExtendedScanParameters(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedScanParametersView::Create(
+      gd_hci::LeScanningCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto parameters = command_view.GetParameters();
+  // Multiple phys are not supported.
+  ASSERT(command_view.GetScanningPhys() == 1);
+  ASSERT(parameters.size() == 1);
+
+  link_layer_controller_.SetLeScanType(
+      static_cast<uint8_t>(parameters[0].le_scan_type_));
+  link_layer_controller_.SetLeScanInterval(parameters[0].le_scan_interval_);
+  link_layer_controller_.SetLeScanWindow(parameters[0].le_scan_window_);
+  link_layer_controller_.SetLeAddressType(
+      static_cast<uint8_t>(command_view.GetOwnAddressType()));
+  link_layer_controller_.SetLeScanFilterPolicy(
+      static_cast<uint8_t>(command_view.GetScanningFilterPolicy()));
+  auto packet =
+      bluetooth::hci::LeSetExtendedScanParametersCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeSetExtendedScanEnable(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedScanEnableView::Create(
+      gd_hci::LeScanningCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  if (command_view.GetEnable() == gd_hci::Enable::ENABLED) {
+    link_layer_controller_.SetLeScanEnable(
+        gd_hci::OpCode::LE_SET_EXTENDED_SCAN_ENABLE);
+  } else {
+    link_layer_controller_.SetLeScanEnable(gd_hci::OpCode::NONE);
+  }
+  link_layer_controller_.SetLeFilterDuplicates(
+      command_view.GetFilterDuplicates() == gd_hci::FilterDuplicates::ENABLED);
+  auto packet = bluetooth::hci::LeSetExtendedScanEnableCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeExtendedCreateConnection(CommandPacketView command) {
+  auto command_view = gd_hci::LeExtendedCreateConnectionView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  ASSERT_LOG(command_view.GetInitiatingPhys() == 1, "Only LE_1M is supported");
+  auto params = command_view.GetPhyScanParameters();
+  link_layer_controller_.SetLeScanInterval(params[0].scan_interval_);
+  link_layer_controller_.SetLeScanWindow(params[0].scan_window_);
+  auto initiator_filter_policy = command_view.GetInitiatorFilterPolicy();
+  link_layer_controller_.SetLeInitiatorFilterPolicy(
+      static_cast<uint8_t>(initiator_filter_policy));
+
+  if (initiator_filter_policy ==
+      gd_hci::InitiatorFilterPolicy::USE_PEER_ADDRESS) {
+    link_layer_controller_.SetLePeerAddressType(
+        static_cast<uint8_t>(command_view.GetPeerAddressType()));
+    link_layer_controller_.SetLePeerAddress(command_view.GetPeerAddress());
+  }
+  link_layer_controller_.SetLeAddressType(
+      static_cast<uint8_t>(command_view.GetOwnAddressType()));
+  link_layer_controller_.SetLeConnectionIntervalMin(
+      params[0].conn_interval_min_);
+  link_layer_controller_.SetLeConnectionIntervalMax(
+      params[0].conn_interval_max_);
+  link_layer_controller_.SetLeConnectionLatency(params[0].conn_latency_);
+  link_layer_controller_.SetLeSupervisionTimeout(
+      params[0].supervision_timeout_);
+  link_layer_controller_.SetLeMinimumCeLength(params[0].min_ce_length_);
+  link_layer_controller_.SetLeMaximumCeLength(params[0].max_ce_length_);
+
+  auto status = link_layer_controller_.SetLeConnect(true);
+
+  send_event_(bluetooth::hci::LeExtendedCreateConnectionStatusBuilder::Create(
+      status, kNumCommandPackets));
+}
+
 void DualModeController::LeSetPrivacyMode(CommandPacketView command) {
   auto command_view = gd_hci::LeSetPrivacyModeView::Create(
       gd_hci::LeSecurityCommandView::Create(command));
@@ -1666,6 +1946,129 @@
       static_cast<uint16_t>(OpCode::LE_ENERGY_INFO));
 }
 
+void DualModeController::LeSetExtendedAdvertisingRandomAddress(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedAdvertisingRandomAddressView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  link_layer_controller_.SetLeExtendedAddress(
+      command_view.GetAdvertisingHandle(),
+      command_view.GetAdvertisingRandomAddress());
+  send_event_(
+      bluetooth::hci::LeSetExtendedAdvertisingRandomAddressCompleteBuilder::
+          Create(kNumCommandPackets, ErrorCode::SUCCESS));
+}
+
+void DualModeController::LeSetExtendedAdvertisingParameters(
+    CommandPacketView command) {
+  auto command_view =
+      gd_hci::LeSetExtendedAdvertisingLegacyParametersView::Create(
+          gd_hci::LeAdvertisingCommandView::Create(command));
+  // TODO: Support non-legacy parameters
+  ASSERT(command_view.IsValid());
+  link_layer_controller_.SetLeExtendedAdvertisingParameters(
+      command_view.GetAdvertisingHandle(),
+      command_view.GetPrimaryAdvertisingIntervalMin(),
+      command_view.GetPrimaryAdvertisingIntervalMax(),
+      command_view.GetAdvertisingEventLegacyProperties(),
+      command_view.GetOwnAddressType(), command_view.GetPeerAddressType(),
+      command_view.GetPeerAddress(), command_view.GetAdvertisingFilterPolicy());
+
+  send_event_(
+      bluetooth::hci::LeSetExtendedAdvertisingParametersCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS, 0xa5));
+}
+
+void DualModeController::LeSetExtendedAdvertisingData(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedAdvertisingDataView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto raw_command_view = gd_hci::LeSetExtendedAdvertisingDataRawView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(raw_command_view.IsValid());
+  link_layer_controller_.SetLeExtendedAdvertisingData(
+      command_view.GetAdvertisingHandle(),
+      raw_command_view.GetAdvertisingData());
+  auto packet =
+      bluetooth::hci::LeSetExtendedAdvertisingDataCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeSetExtendedAdvertisingScanResponse(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedAdvertisingScanResponseView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  properties_.SetLeScanResponse(std::vector<uint8_t>(
+      command_view.GetPayload().begin() + 1, command_view.GetPayload().end()));
+  send_event_(
+      bluetooth::hci::LeSetExtendedAdvertisingScanResponseCompleteBuilder::
+          Create(kNumCommandPackets, ErrorCode::SUCCESS));
+}
+
+void DualModeController::LeSetExtendedAdvertisingEnable(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedAdvertisingEnableView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto enabled_sets = command_view.GetEnabledSets();
+  ErrorCode status = ErrorCode::SUCCESS;
+  if (enabled_sets.size() == 0) {
+    link_layer_controller_.LeDisableAdvertisingSets();
+  } else {
+    status = link_layer_controller_.SetLeExtendedAdvertisingEnable(
+        command_view.GetEnable(), command_view.GetEnabledSets());
+  }
+  send_event_(
+      bluetooth::hci::LeSetExtendedAdvertisingEnableCompleteBuilder::Create(
+          kNumCommandPackets, status));
+}
+
+void DualModeController::LeReadMaximumAdvertisingDataLength(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeReadMaximumAdvertisingDataLengthView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  send_event_(
+      bluetooth::hci::LeReadMaximumAdvertisingDataLengthCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS,
+          kLeMaximumAdvertisingDataLength));
+}
+
+void DualModeController::LeReadNumberOfSupportedAdvertisingSets(
+    CommandPacketView command) {
+  auto command_view =
+      gd_hci::LeReadNumberOfSupportedAdvertisingSetsView::Create(
+          gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  send_event_(
+      bluetooth::hci::LeReadNumberOfSupportedAdvertisingSetsCompleteBuilder::
+          Create(
+              kNumCommandPackets, ErrorCode::SUCCESS,
+              link_layer_controller_.LeReadNumberOfSupportedAdvertisingSets()));
+}
+
+void DualModeController::LeRemoveAdvertisingSet(CommandPacketView command) {
+  auto command_view = gd_hci::LeRemoveAdvertisingSetView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto status = link_layer_controller_.LeRemoveAdvertisingSet(
+      command_view.GetAdvertisingHandle());
+  send_event_(bluetooth::hci::LeRemoveAdvertisingSetCompleteBuilder::Create(
+      kNumCommandPackets, status));
+}
+
+void DualModeController::LeClearAdvertisingSets(CommandPacketView command) {
+  auto command_view = gd_hci::LeClearAdvertisingSetsView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto status = link_layer_controller_.LeClearAdvertisingSets();
+  send_event_(bluetooth::hci::LeClearAdvertisingSetsCompleteBuilder::Create(
+      kNumCommandPackets, status));
+}
+
 void DualModeController::LeExtendedScanParams(CommandPacketView command) {
   auto command_view = gd_hci::LeExtendedScanParamsView::Create(
       gd_hci::LeScanningCommandView::Create(command));
@@ -1679,15 +2082,12 @@
       gd_hci::LeSecurityCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
-  uint16_t handle = command_view.GetConnectionHandle();
+  ErrorCode status = link_layer_controller_.LeEnableEncryption(
+      command_view.GetConnectionHandle(), command_view.GetRand(),
+      command_view.GetEdiv(), command_view.GetLtk());
 
-  auto status_packet = bluetooth::hci::LeStartEncryptionStatusBuilder::Create(
-      ErrorCode::SUCCESS, kNumCommandPackets);
-  send_event_(std::move(status_packet));
-
-  auto complete_packet = bluetooth::hci::EncryptionChangeBuilder::Create(
-      ErrorCode::SUCCESS, handle, bluetooth::hci::EncryptionEnabled::OFF);
-  send_event_(std::move(complete_packet));
+  send_event_(bluetooth::hci::LeStartEncryptionStatusBuilder::Create(
+      status, kNumCommandPackets));
 }
 
 void DualModeController::ReadLoopbackMode(CommandPacketView command) {
diff --git a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h
index 65c2dad..d59705e 100644
--- a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h
+++ b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h
@@ -205,6 +205,9 @@
   // 7.2.10
   void WriteLinkPolicySettings(CommandPacketView args);
 
+  // 7.2.11
+  void ReadDefaultLinkPolicySettings(CommandPacketView args);
+
   // 7.2.12
   void WriteDefaultLinkPolicySettings(CommandPacketView args);
 
@@ -235,12 +238,27 @@
   // 7.3.12
   void ReadLocalName(CommandPacketView args);
 
+  // 7.3.15
+  void ReadPageTimeout(CommandPacketView args);
+
   // 7.3.16
   void WritePageTimeout(CommandPacketView args);
 
+  // 7.3.17
+  void ReadScanEnable(CommandPacketView args);
+
   // 7.3.18
   void WriteScanEnable(CommandPacketView args);
 
+  // 7.3.19
+  void ReadPageScanActivity(CommandPacketView args);
+
+  // 7.3.20
+  void WritePageScanActivity(CommandPacketView args);
+
+  // 7.3.21
+  void ReadInquiryScanActivity(CommandPacketView args);
+
   // 7.3.22
   void WriteInquiryScanActivity(CommandPacketView args);
 
@@ -262,16 +280,31 @@
   // 7.3.42
   void WriteLinkSupervisionTimeout(CommandPacketView args);
 
+  // 7.3.43
+  void ReadNumberOfSupportedIac(CommandPacketView args);
+
+  // 7.3.44
+  void ReadCurrentIacLap(CommandPacketView args);
+
   // 7.3.45
   void WriteCurrentIacLap(CommandPacketView args);
 
+  // 7.3.47
+  void ReadInquiryScanType(CommandPacketView args);
+
   // 7.3.48
   void WriteInquiryScanType(CommandPacketView args);
 
+  // 7.3.49
+  void ReadInquiryMode(CommandPacketView args);
+
   // 7.3.50
   void WriteInquiryMode(CommandPacketView args);
 
   // 7.3.52
+  void ReadPageScanType(CommandPacketView args);
+
+  // 7.3.52
   void WritePageScanType(CommandPacketView args);
 
   // 7.3.56
@@ -283,6 +316,9 @@
   // 7.3.59
   void WriteSimplePairingMode(CommandPacketView args);
 
+  // 7.3.61
+  void ReadInquiryResponseTransmitPowerLevel(CommandPacketView args);
+
   // 7.3.79
   void WriteLeHostSupport(CommandPacketView args);
 
@@ -371,16 +407,16 @@
   void LeConnectionCancel(CommandPacketView args);
 
   // 7.8.14
-  void LeReadWhiteListSize(CommandPacketView args);
+  void LeReadConnectListSize(CommandPacketView args);
 
   // 7.8.15
-  void LeClearWhiteList(CommandPacketView args);
+  void LeClearConnectList(CommandPacketView args);
 
   // 7.8.16
-  void LeAddDeviceToWhiteList(CommandPacketView args);
+  void LeAddDeviceToConnectList(CommandPacketView args);
 
   // 7.8.17
-  void LeRemoveDeviceFromWhiteList(CommandPacketView args);
+  void LeRemoveDeviceFromConnectList(CommandPacketView args);
 
   // 7.8.21
   void LeReadRemoteFeatures(CommandPacketView args);
@@ -403,6 +439,48 @@
   // 7.8.40
   void LeClearResolvingList(CommandPacketView args);
 
+  // 7.8.41
+  void LeReadResolvingListSize(CommandPacketView args);
+
+  // 7.8.46
+  void LeReadMaximumDataLength(CommandPacketView args);
+
+  // 7.8.52
+  void LeSetExtendedAdvertisingRandomAddress(CommandPacketView args);
+
+  // 7.8.53
+  void LeSetExtendedAdvertisingParameters(CommandPacketView args);
+
+  // 7.8.54
+  void LeSetExtendedAdvertisingData(CommandPacketView args);
+
+  // 7.8.55
+  void LeSetExtendedAdvertisingScanResponse(CommandPacketView args);
+
+  // 7.8.56
+  void LeSetExtendedAdvertisingEnable(CommandPacketView args);
+
+  // 7.8.57
+  void LeReadMaximumAdvertisingDataLength(CommandPacketView args);
+
+  // 7.8.58
+  void LeReadNumberOfSupportedAdvertisingSets(CommandPacketView args);
+
+  // 7.8.59
+  void LeRemoveAdvertisingSet(CommandPacketView args);
+
+  // 7.8.60
+  void LeClearAdvertisingSets(CommandPacketView args);
+
+  // 7.8.64
+  void LeSetExtendedScanParameters(CommandPacketView args);
+
+  // 7.8.65
+  void LeSetExtendedScanEnable(CommandPacketView args);
+
+  // 7.8.66
+  void LeExtendedCreateConnection(CommandPacketView args);
+
   // 7.8.77
   void LeSetPrivacyMode(CommandPacketView args);
 
diff --git a/vendor_libs/test_vendor_lib/model/controller/le_advertiser.cc b/vendor_libs/test_vendor_lib/model/controller/le_advertiser.cc
new file mode 100644
index 0000000..09a9503
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/model/controller/le_advertiser.cc
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#include "le_advertiser.h"
+
+using namespace bluetooth::hci;
+
+namespace test_vendor_lib {
+void LeAdvertiser::Initialize(AddressWithType address,
+                              AddressWithType peer_address,
+                              LeScanningFilterPolicy filter_policy,
+                              model::packets::AdvertisementType type,
+                              const std::vector<uint8_t>& advertisement,
+                              const std::vector<uint8_t>& scan_response,
+                              std::chrono::steady_clock::duration interval) {
+  address_ = address;
+  peer_address_ = peer_address;
+  filter_policy_ = filter_policy;
+  type_ = type;
+  advertisement_ = advertisement;
+  scan_response_ = scan_response;
+  interval_ = interval;
+}
+
+void LeAdvertiser::InitializeExtended(
+    AddressType address_type, AddressWithType peer_address,
+    LeScanningFilterPolicy filter_policy,
+    model::packets::AdvertisementType type,
+    std::chrono::steady_clock::duration interval) {
+  address_ = AddressWithType(address_.GetAddress(), address_type);
+  peer_address_ = peer_address;
+  filter_policy_ = filter_policy;
+  type_ = type;
+  interval_ = interval;
+  LOG_INFO("%s -> %s type = %hhx interval = %d ms", address_.ToString().c_str(),
+           peer_address.ToString().c_str(), type_,
+           static_cast<int>(interval_.count()));
+}
+
+void LeAdvertiser::Clear() {
+  address_ = AddressWithType{};
+  peer_address_ = AddressWithType{};
+  filter_policy_ = LeScanningFilterPolicy::ACCEPT_ALL;
+  type_ = model::packets::AdvertisementType::ADV_IND;
+  advertisement_.clear();
+  scan_response_.clear();
+  interval_ = std::chrono::milliseconds(0);
+  enabled_ = false;
+}
+
+void LeAdvertiser::SetAddress(Address address) {
+  LOG_INFO("set address %s", address_.ToString().c_str());
+  address_ = AddressWithType(address, address_.GetAddressType());
+}
+
+AddressWithType LeAdvertiser::GetAddress() const { return address_; }
+
+void LeAdvertiser::SetData(const std::vector<uint8_t>& data) {
+  advertisement_ = data;
+}
+
+void LeAdvertiser::Enable() {
+  enabled_ = true;
+  last_le_advertisement_ = std::chrono::steady_clock::now() - interval_;
+  LOG_INFO("%s -> %s type = %hhx ad length %zu, scan length %zu",
+           address_.ToString().c_str(), peer_address_.ToString().c_str(), type_,
+           advertisement_.size(), scan_response_.size());
+}
+
+void LeAdvertiser::EnableExtended(
+    std::chrono::steady_clock::duration duration) {
+  last_le_advertisement_ = std::chrono::steady_clock::now();
+  if (duration != std::chrono::milliseconds(0)) {
+    ending_time_ = std::chrono::steady_clock::now() + duration;
+  }
+  enabled_ = true;
+  LOG_INFO("%s -> %s type = %hhx ad length %zu, scan length %zu",
+           address_.ToString().c_str(), peer_address_.ToString().c_str(), type_,
+           advertisement_.size(), scan_response_.size());
+}
+
+void LeAdvertiser::Disable() { enabled_ = false; }
+
+bool LeAdvertiser::IsEnabled() const { return enabled_; }
+
+std::unique_ptr<model::packets::LeAdvertisementBuilder>
+LeAdvertiser::GetAdvertisement(std::chrono::steady_clock::time_point now) {
+  if (!enabled_) {
+    return nullptr;
+  }
+
+  if (now - last_le_advertisement_ < interval_) {
+    return nullptr;
+  }
+
+  if (last_le_advertisement_ < ending_time_ && ending_time_ < now) {
+    enabled_ = false;
+    return nullptr;
+  }
+
+  last_le_advertisement_ = now;
+  return model::packets::LeAdvertisementBuilder::Create(
+      address_.GetAddress(), peer_address_.GetAddress(),
+      static_cast<model::packets::AddressType>(address_.GetAddressType()),
+      type_, advertisement_);
+}
+
+std::unique_ptr<model::packets::LeScanResponseBuilder>
+LeAdvertiser::GetScanResponse(bluetooth::hci::Address scanned,
+                              bluetooth::hci::Address scanner) {
+  if (scanned != address_.GetAddress() || !enabled_ || scan_response_.empty()) {
+    return nullptr;
+  }
+  switch (filter_policy_) {
+    case bluetooth::hci::LeScanningFilterPolicy::
+        CONNECT_LIST_AND_INITIATORS_IDENTITY:
+    case bluetooth::hci::LeScanningFilterPolicy::CONNECT_LIST_ONLY:
+      LOG_WARN("ScanResponses don't handle connect list filters");
+      return nullptr;
+    case bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY:
+      if (scanner != peer_address_.GetAddress()) {
+        return nullptr;
+      }
+      break;
+    case bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL:
+      break;
+  }
+  return model::packets::LeScanResponseBuilder::Create(
+      address_.GetAddress(), peer_address_.GetAddress(),
+      static_cast<model::packets::AddressType>(address_.GetAddressType()),
+      model::packets::AdvertisementType::SCAN_RESPONSE, scan_response_);
+}
+
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/le_advertiser.h b/vendor_libs/test_vendor_lib/model/controller/le_advertiser.h
new file mode 100644
index 0000000..264f15e
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/model/controller/le_advertiser.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <cstdint>
+#include <memory>
+
+#include "hci/address_with_type.h"
+#include "hci/hci_packets.h"
+#include "packets/link_layer_packets.h"
+
+namespace test_vendor_lib {
+
+// Track a single advertising instance
+class LeAdvertiser {
+ public:
+  LeAdvertiser() = default;
+  virtual ~LeAdvertiser() = default;
+
+  void Initialize(bluetooth::hci::AddressWithType address,
+                  bluetooth::hci::AddressWithType peer_address,
+                  bluetooth::hci::LeScanningFilterPolicy filter_policy,
+                  model::packets::AdvertisementType type,
+                  const std::vector<uint8_t>& advertisement,
+                  const std::vector<uint8_t>& scan_response,
+                  std::chrono::steady_clock::duration interval);
+
+  void InitializeExtended(bluetooth::hci::AddressType address_type,
+                          bluetooth::hci::AddressWithType peer_address,
+                          bluetooth::hci::LeScanningFilterPolicy filter_policy,
+                          model::packets::AdvertisementType type,
+                          std::chrono::steady_clock::duration interval);
+
+  void SetAddress(bluetooth::hci::Address address);
+
+  void SetData(const std::vector<uint8_t>& data);
+
+  std::unique_ptr<model::packets::LeAdvertisementBuilder> GetAdvertisement(
+      std::chrono::steady_clock::time_point);
+
+  std::unique_ptr<model::packets::LeScanResponseBuilder> GetScanResponse(
+      bluetooth::hci::Address scanned_address,
+      bluetooth::hci::Address scanner_address);
+
+  void Clear();
+
+  void Disable();
+
+  void Enable();
+
+  void EnableExtended(std::chrono::steady_clock::duration duration);
+
+  bool IsEnabled() const;
+
+  bluetooth::hci::AddressWithType GetAddress() const;
+
+ private:
+  bluetooth::hci::AddressWithType address_{};
+  bluetooth::hci::AddressWithType
+      peer_address_{};  // For directed advertisements
+  bluetooth::hci::LeScanningFilterPolicy filter_policy_{};
+  model::packets::AdvertisementType type_{};
+  std::vector<uint8_t> advertisement_;
+  std::vector<uint8_t> scan_response_;
+  std::chrono::steady_clock::duration interval_{};
+  std::chrono::steady_clock::time_point ending_time_{};
+  bool enabled_{false};
+  std::chrono::steady_clock::time_point last_le_advertisement_;
+};
+
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
index 2c78ffb..125b858 100644
--- a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
@@ -15,6 +15,7 @@
  */
 
 #include "link_layer_controller.h"
+#include <hci/hci_packets.h>
 
 #include "include/le_advertisement.h"
 #include "os/log.h"
@@ -22,6 +23,9 @@
 
 using std::vector;
 using namespace std::chrono;
+using bluetooth::hci::Address;
+using bluetooth::hci::AddressType;
+using bluetooth::hci::AddressWithType;
 
 namespace test_vendor_lib {
 
@@ -119,10 +123,7 @@
   AddressWithType destination = connections_.GetAddress(handle);
   Phy::Type phy = connections_.GetPhyType(handle);
 
-  LOG_INFO("%s(%s): handle 0x%x size %d", __func__, properties_.GetAddress().ToString().c_str(), handle,
-           static_cast<int>(acl_packet.size()));
-
-  ScheduleTask(milliseconds(5), [this, handle]() {
+  ScheduleTask(milliseconds(1), [this, handle]() {
     std::vector<bluetooth::hci::CompletedPackets> completed_packets;
     bluetooth::hci::CompletedPackets cp;
     cp.connection_handle_ = handle;
@@ -165,12 +166,27 @@
 void LinkLayerController::IncomingPacket(
     model::packets::LinkLayerPacketView incoming) {
   ASSERT(incoming.IsValid());
+  auto destination_address = incoming.GetDestinationAddress();
 
-  // TODO: Resolvable private addresses?
-  if (incoming.GetDestinationAddress() != properties_.GetAddress() &&
-      incoming.GetDestinationAddress() != properties_.GetLeAddress() &&
-      incoming.GetDestinationAddress() != Address::kEmpty) {
-    // Drop packets not addressed to me
+  // Match broadcasts
+  bool address_matches = (destination_address == Address::kEmpty);
+
+  // Match addresses from device properties
+  if (destination_address == properties_.GetAddress() ||
+      destination_address == properties_.GetLeAddress()) {
+    address_matches = true;
+  }
+
+  // Check advertising addresses
+  for (const auto& advertiser : advertisers_) {
+    if (advertiser.IsEnabled() &&
+        advertiser.GetAddress().GetAddress() == destination_address) {
+      address_matches = true;
+    }
+  }
+
+  // Drop packets not addressed to me
+  if (!address_matches) {
     return;
   }
 
@@ -205,7 +221,7 @@
       IncomingIoCapabilityNegativeResponsePacket(incoming);
       break;
     case model::packets::PacketType::LE_ADVERTISEMENT:
-      if (le_scan_enable_ || le_connect_) {
+      if (le_scan_enable_ != bluetooth::hci::OpCode::NONE || le_connect_) {
         IncomingLeAdvertisementPacket(incoming);
       }
       break;
@@ -215,12 +231,19 @@
     case model::packets::PacketType::LE_CONNECT_COMPLETE:
       IncomingLeConnectCompletePacket(incoming);
       break;
+    case model::packets::PacketType::LE_ENCRYPT_CONNECTION:
+      IncomingLeEncryptConnection(incoming);
+      break;
+    case model::packets::PacketType::LE_ENCRYPT_CONNECTION_RESPONSE:
+      IncomingLeEncryptConnectionResponse(incoming);
+      break;
     case model::packets::PacketType::LE_SCAN:
       // TODO: Check Advertising flags and see if we are scannable.
       IncomingLeScanPacket(incoming);
       break;
     case model::packets::PacketType::LE_SCAN_RESPONSE:
-      if (le_scan_enable_ && le_scan_type_ == 1) {
+      if (le_scan_enable_ != bluetooth::hci::OpCode::NONE &&
+          le_scan_type_ == 1) {
         IncomingLeScanResponsePacket(incoming);
       }
       break;
@@ -279,7 +302,8 @@
 
 void LinkLayerController::IncomingAclPacket(
     model::packets::LinkLayerPacketView incoming) {
-  LOG_INFO("Acl Packet %s -> %s", incoming.GetSourceAddress().ToString().c_str(),
+  LOG_INFO("Acl Packet %s -> %s",
+           incoming.GetSourceAddress().ToString().c_str(),
            incoming.GetDestinationAddress().ToString().c_str());
 
   auto acl = model::packets::AclPacketView::Create(incoming);
@@ -293,22 +317,40 @@
   auto acl_view = bluetooth::hci::AclPacketView::Create(raw_packet);
   ASSERT(acl_view.IsValid());
 
-  LOG_INFO("%s: remote handle 0x%x size %d", __func__, acl_view.GetHandle(),
+  LOG_INFO("Remote handle 0x%x size %d", acl_view.GetHandle(),
            static_cast<int>(acl_view.size()));
   uint16_t local_handle =
       connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
-  LOG_INFO("%s: local handle 0x%x", __func__, local_handle);
+  LOG_INFO("Local handle 0x%x", local_handle);
 
   std::vector<uint8_t> payload_data(acl_view.GetPayload().begin(),
                                     acl_view.GetPayload().end());
-  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-      std::make_unique<bluetooth::packet::RawBuilder>(payload_data);
+  uint16_t acl_buffer_size = properties_.GetAclDataPacketSize();
+  int num_packets =
+      (payload_data.size() + acl_buffer_size - 1) / acl_buffer_size;
 
-  auto acl_packet = bluetooth::hci::AclPacketBuilder::Create(
-      local_handle, acl_view.GetPacketBoundaryFlag(),
-      acl_view.GetBroadcastFlag(), std::move(raw_builder_ptr));
+  auto pb_flag_controller_to_host = acl_view.GetPacketBoundaryFlag();
+  if (pb_flag_controller_to_host ==
+      bluetooth::hci::PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE) {
+    pb_flag_controller_to_host =
+        bluetooth::hci::PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+  }
+  for (int i = 0; i < num_packets; i++) {
+    size_t start_index = acl_buffer_size * i;
+    size_t end_index =
+        std::min(start_index + acl_buffer_size, payload_data.size());
+    std::vector<uint8_t> fragment(&payload_data[start_index],
+                                  &payload_data[end_index]);
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>(fragment);
+    auto acl_packet = bluetooth::hci::AclPacketBuilder::Create(
+        local_handle, pb_flag_controller_to_host, acl_view.GetBroadcastFlag(),
+        std::move(raw_builder_ptr));
+    pb_flag_controller_to_host =
+        bluetooth::hci::PacketBoundaryFlag::CONTINUING_FRAGMENT;
 
-  send_acl_(std::move(acl_packet));
+    send_acl_(std::move(acl_packet));
+  }
 }
 
 void LinkLayerController::IncomingRemoteNameRequest(
@@ -400,10 +442,9 @@
              source.ToString().c_str());
     return;
   }
-    send_event_(
-        bluetooth::hci::ReadRemoteExtendedFeaturesCompleteBuilder::Create(
-            static_cast<ErrorCode>(view.GetStatus()), handle,
-            view.GetPageNumber(), view.GetMaxPageNumber(), view.GetFeatures()));
+  send_event_(bluetooth::hci::ReadRemoteExtendedFeaturesCompleteBuilder::Create(
+      static_cast<ErrorCode>(view.GetStatus()), handle, view.GetPageNumber(),
+      view.GetMaxPageNumber(), view.GetFeatures()));
 }
 
 void LinkLayerController::IncomingReadRemoteVersion(
@@ -426,10 +467,10 @@
              source.ToString().c_str());
     return;
   }
-    send_event_(
-        bluetooth::hci::ReadRemoteVersionInformationCompleteBuilder::Create(
-            ErrorCode::SUCCESS, handle, view.GetLmpVersion(),
-            view.GetManufacturerName(), view.GetLmpSubversion()));
+  send_event_(
+      bluetooth::hci::ReadRemoteVersionInformationCompleteBuilder::Create(
+          ErrorCode::SUCCESS, handle, view.GetLmpVersion(),
+          view.GetManufacturerName(), view.GetLmpSubversion()));
 }
 
 void LinkLayerController::IncomingReadClockOffset(
@@ -467,21 +508,23 @@
              peer.ToString().c_str());
     return;
   }
-  ASSERT_LOG(connections_.Disconnect(handle), "GetHandle() returned invalid handle %hx", handle);
+  ASSERT_LOG(connections_.Disconnect(handle),
+             "GetHandle() returned invalid handle %hx", handle);
 
   uint8_t reason = disconnect.GetReason();
-  ScheduleTask(milliseconds(20), [this, handle, reason]() { DisconnectCleanup(handle, reason); });
+  ScheduleTask(milliseconds(20),
+               [this, handle, reason]() { DisconnectCleanup(handle, reason); });
 }
 
 void LinkLayerController::IncomingEncryptConnection(
     model::packets::LinkLayerPacketView incoming) {
-  LOG_INFO("%s", __func__);
+  LOG_INFO();
 
   // TODO: Check keys
   Address peer = incoming.GetSourceAddress();
   uint16_t handle = connections_.GetHandleOnlyAddress(peer);
   if (handle == acl::kReservedHandle) {
-    LOG_INFO("%s: Unknown connection @%s", __func__, peer.ToString().c_str());
+    LOG_INFO("Unknown connection @%s", peer.ToString().c_str());
     return;
   }
   send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
@@ -501,12 +544,13 @@
 
 void LinkLayerController::IncomingEncryptConnectionResponse(
     model::packets::LinkLayerPacketView incoming) {
-  LOG_INFO("%s", __func__);
+  LOG_INFO();
   // TODO: Check keys
   uint16_t handle =
       connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
   if (handle == acl::kReservedHandle) {
-    LOG_INFO("%s: Unknown connection @%s", __func__, incoming.GetSourceAddress().ToString().c_str());
+    LOG_INFO("Unknown connection @%s",
+             incoming.GetSourceAddress().ToString().c_str());
     return;
   }
   auto packet = bluetooth::hci::EncryptionChangeBuilder::Create(
@@ -549,7 +593,8 @@
 
     } break;
     default:
-      LOG_WARN("Unhandled Incoming Inquiry of type %d", static_cast<int>(inquiry.GetType()));
+      LOG_WARN("Unhandled Incoming Inquiry of type %d",
+               static_cast<int>(inquiry.GetType()));
       return;
   }
   // TODO: Send an Inquiry Response Notification Event 7.7.74
@@ -573,11 +618,13 @@
           (bluetooth::hci::PageScanRepetitionMode)
               inquiry_response.GetPageScanRepetitionMode();
 
-      auto packet = bluetooth::hci::InquiryResultBuilder::Create(
-          0x01, inquiry_response.GetSourceAddress(), page_scan_repetition_mode,
-          inquiry_response.GetClassOfDevice(),
-          inquiry_response.GetClockOffset());
-
+      std::vector<bluetooth::hci::InquiryResult> responses;
+      responses.emplace_back();
+      responses.back().bd_addr_ = inquiry_response.GetSourceAddress();
+      responses.back().page_scan_repetition_mode_ = page_scan_repetition_mode;
+      responses.back().class_of_device_ = inquiry_response.GetClassOfDevice();
+      responses.back().clock_offset_ = inquiry_response.GetClockOffset();
+      auto packet = bluetooth::hci::InquiryResultBuilder::Create(responses);
       send_event_(std::move(packet));
     } break;
 
@@ -591,10 +638,15 @@
           (bluetooth::hci::PageScanRepetitionMode)
               inquiry_response.GetPageScanRepetitionMode();
 
-      auto packet = bluetooth::hci::InquiryResultWithRssiBuilder::Create(
-          0x01, inquiry_response.GetSourceAddress(), page_scan_repetition_mode,
-          inquiry_response.GetClassOfDevice(),
-          inquiry_response.GetClockOffset(), inquiry_response.GetRssi());
+      std::vector<bluetooth::hci::InquiryResultWithRssi> responses;
+      responses.emplace_back();
+      responses.back().address_ = inquiry_response.GetSourceAddress();
+      responses.back().page_scan_repetition_mode_ = page_scan_repetition_mode;
+      responses.back().class_of_device_ = inquiry_response.GetClassOfDevice();
+      responses.back().clock_offset_ = inquiry_response.GetClockOffset();
+      responses.back().rssi_ = inquiry_response.GetRssi();
+      auto packet =
+          bluetooth::hci::InquiryResultWithRssiBuilder::Create(responses);
       send_event_(std::move(packet));
     } break;
 
@@ -631,9 +683,9 @@
 
 void LinkLayerController::IncomingIoCapabilityRequestPacket(
     model::packets::LinkLayerPacketView incoming) {
-  LOG_DEBUG("%s", __func__);
+  LOG_DEBUG();
   if (!simple_pairing_mode_enabled_) {
-    LOG_WARN("%s: Only simple pairing mode is implemented", __func__);
+    LOG_WARN("Only simple pairing mode is implemented");
     return;
   }
 
@@ -648,13 +700,14 @@
   uint16_t handle = connections_.GetHandle(AddressWithType(
       peer, bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS));
   if (handle == acl::kReservedHandle) {
-    LOG_INFO("%s: Device not connected %s", __func__, peer.ToString().c_str());
+    LOG_INFO("Device not connected %s", peer.ToString().c_str());
     return;
   }
 
   security_manager_.AuthenticationRequest(peer, handle);
 
-  security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present, authentication_requirements);
+  security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present,
+                                        authentication_requirements);
 
   auto packet = bluetooth::hci::IoCapabilityResponseBuilder::Create(
       peer, static_cast<bluetooth::hci::IoCapability>(io_capability),
@@ -668,7 +721,7 @@
 
 void LinkLayerController::IncomingIoCapabilityResponsePacket(
     model::packets::LinkLayerPacketView incoming) {
-  LOG_DEBUG("%s", __func__);
+  LOG_DEBUG();
 
   auto response = model::packets::IoCapabilityResponseView::Create(incoming);
   ASSERT(response.IsValid());
@@ -676,7 +729,8 @@
   Address peer = incoming.GetSourceAddress();
   uint8_t io_capability = response.GetIoCapability();
   uint8_t oob_data_present = response.GetOobDataPresent();
-  uint8_t authentication_requirements = response.GetAuthenticationRequirements();
+  uint8_t authentication_requirements =
+      response.GetAuthenticationRequirements();
 
   security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present,
                                         authentication_requirements);
@@ -694,13 +748,13 @@
       AuthenticateRemoteStage1(peer, pairing_type);
     });
   } else {
-    LOG_INFO("%s: Security Manager returned INVALID", __func__);
+    LOG_INFO("Security Manager returned INVALID");
   }
 }
 
 void LinkLayerController::IncomingIoCapabilityNegativeResponsePacket(
     model::packets::LinkLayerPacketView incoming) {
-  LOG_DEBUG("%s", __func__);
+  LOG_DEBUG();
   Address peer = incoming.GetSourceAddress();
 
   ASSERT(security_manager_.GetAuthenticationAddress() == peer);
@@ -715,11 +769,10 @@
   Address address = incoming.GetSourceAddress();
   auto advertisement = model::packets::LeAdvertisementView::Create(incoming);
   ASSERT(advertisement.IsValid());
-  auto adv_type = static_cast<LeAdvertisement::AdvertisementType>(
-      advertisement.GetAdvertisementType());
   auto address_type = advertisement.GetAddressType();
+  auto adv_type = advertisement.GetAdvertisementType();
 
-  if (le_scan_enable_) {
+  if (le_scan_enable_ == bluetooth::hci::OpCode::LE_SET_SCAN_ENABLE) {
     vector<uint8_t> ad = advertisement.GetData();
 
     std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
@@ -738,8 +791,50 @@
     send_event_(std::move(packet));
   }
 
+  if (le_scan_enable_ == bluetooth::hci::OpCode::LE_SET_EXTENDED_SCAN_ENABLE) {
+    vector<uint8_t> ad = advertisement.GetData();
+
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>();
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(
+        bluetooth::hci::SubeventCode::EXTENDED_ADVERTISING_REPORT));
+    raw_builder_ptr->AddOctets1(0x01);  // num reports
+    switch (adv_type) {
+      case model::packets::AdvertisementType::ADV_IND:
+        raw_builder_ptr->AddOctets1(0x13);
+        break;
+      case model::packets::AdvertisementType::ADV_DIRECT_IND:
+        raw_builder_ptr->AddOctets1(0x15);
+        break;
+      case model::packets::AdvertisementType::ADV_SCAN_IND:
+        raw_builder_ptr->AddOctets1(0x12);
+        break;
+      case model::packets::AdvertisementType::ADV_NONCONN_IND:
+        raw_builder_ptr->AddOctets1(0x10);
+        break;
+      case model::packets::AdvertisementType::SCAN_RESPONSE:
+        raw_builder_ptr->AddOctets1(0x1b);  // 0x1a for ADV_SCAN_IND scan
+        return;
+    }
+    raw_builder_ptr->AddOctets1(0x00);  // Reserved
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(address_type));
+    raw_builder_ptr->AddAddress(address);
+    raw_builder_ptr->AddOctets1(1);     // Primary_PHY
+    raw_builder_ptr->AddOctets1(0);     // Secondary_PHY
+    raw_builder_ptr->AddOctets1(0xFF);  // Advertising_SID - not provided
+    raw_builder_ptr->AddOctets1(0x7F);  // Tx_Power - Not available
+    raw_builder_ptr->AddOctets1(GetRssi());
+    raw_builder_ptr->AddOctets2(0);  // Periodic_Advertising_Interval - None
+    raw_builder_ptr->AddOctets1(0);  // Direct_Address_Type - PUBLIC
+    raw_builder_ptr->AddAddress(Address::kEmpty);  // Direct_Address
+    raw_builder_ptr->AddOctets1(ad.size());
+    raw_builder_ptr->AddOctets(ad);
+    send_event_(bluetooth::hci::EventPacketBuilder::Create(
+        bluetooth::hci::EventCode::LE_META_EVENT, std::move(raw_builder_ptr)));
+  }
+
   // Active scanning
-  if (le_scan_enable_ && le_scan_type_ == 1) {
+  if (le_scan_enable_ != bluetooth::hci::OpCode::NONE && le_scan_type_ == 1) {
     auto to_send = model::packets::LeScanBuilder::Create(
         properties_.GetLeAddress(), address);
     SendLeLinkLayerPacket(std::move(to_send));
@@ -748,22 +843,21 @@
   // Connect
   if ((le_connect_ && le_peer_address_ == address &&
        le_peer_address_type_ == static_cast<uint8_t>(address_type) &&
-       (adv_type == LeAdvertisement::AdvertisementType::ADV_IND ||
-        adv_type == LeAdvertisement::AdvertisementType::ADV_DIRECT_IND)) ||
-      (LeWhiteListContainsDevice(address,
-                                 static_cast<uint8_t>(address_type)))) {
+       (adv_type == model::packets::AdvertisementType::ADV_IND ||
+        adv_type == model::packets::AdvertisementType::ADV_DIRECT_IND)) ||
+      (LeConnectListContainsDevice(address,
+                                   static_cast<uint8_t>(address_type)))) {
     if (!connections_.CreatePendingLeConnection(AddressWithType(
             address, static_cast<bluetooth::hci::AddressType>(address_type)))) {
       LOG_WARN(
-          "%s: CreatePendingLeConnection failed for connection to %s (type "
-          "%hhx)",
-          __func__, incoming.GetSourceAddress().ToString().c_str(),
-          address_type);
+          "CreatePendingLeConnection failed for connection to %s (type %hhx)",
+          incoming.GetSourceAddress().ToString().c_str(), address_type);
     }
-    LOG_INFO("%s: connecting to %s (type %hhx)", __func__, incoming.GetSourceAddress().ToString().c_str(),
-             address_type);
+    LOG_INFO("Connecting to %s (type %hhx) own_address %s (type %hhx)",
+             incoming.GetSourceAddress().ToString().c_str(), address_type,
+             properties_.GetLeAddress().ToString().c_str(), le_address_type_);
     le_connect_ = false;
-    le_scan_enable_ = false;
+    le_scan_enable_ = bluetooth::hci::OpCode::NONE;
 
     auto to_send = model::packets::LeConnectBuilder::Create(
         properties_.GetLeAddress(), incoming.GetSourceAddress(),
@@ -784,7 +878,7 @@
   // TODO: Choose between LeConnectionComplete and LeEnhancedConnectionComplete
   uint16_t handle = connections_.CreateLeConnection(address, own_address);
   if (handle == acl::kReservedHandle) {
-    LOG_WARN("%s: No pending connection for connection from %s", __func__,
+    LOG_WARN("No pending connection for connection from %s",
              address.ToString().c_str());
     return;
   }
@@ -792,7 +886,7 @@
       ErrorCode::SUCCESS, handle, static_cast<bluetooth::hci::Role>(role),
       address.GetAddressType(), address.GetAddress(), connection_interval,
       connection_latency, supervision_timeout,
-      static_cast<bluetooth::hci::MasterClockAccuracy>(0x00));
+      static_cast<bluetooth::hci::ClockAccuracy>(0x00));
   send_event_(std::move(packet));
 }
 
@@ -807,28 +901,41 @@
           incoming.GetSourceAddress(), static_cast<bluetooth::hci::AddressType>(
                                            connect.GetAddressType())))) {
     LOG_WARN(
-        "%s: CreatePendingLeConnection failed for connection from %s (type "
+        "CreatePendingLeConnection failed for connection from %s (type "
         "%hhx)",
-        __func__, incoming.GetSourceAddress().ToString().c_str(),
+        incoming.GetSourceAddress().ToString().c_str(),
         connect.GetAddressType());
     return;
   }
+  bluetooth::hci::AddressWithType my_address{};
+  bool matched_advertiser = false;
+  for (auto advertiser : advertisers_) {
+    AddressWithType advertiser_address = advertiser.GetAddress();
+    if (incoming.GetDestinationAddress() == advertiser_address.GetAddress()) {
+      my_address = advertiser_address;
+      matched_advertiser = true;
+    }
+  }
+
+  if (!matched_advertiser) {
+    LOG_INFO("Dropping unmatched connection request to %s",
+             incoming.GetSourceAddress().ToString().c_str());
+    return;
+  }
+
   HandleLeConnection(
       AddressWithType(
           incoming.GetSourceAddress(),
           static_cast<bluetooth::hci::AddressType>(connect.GetAddressType())),
-      AddressWithType(incoming.GetDestinationAddress(),
-                      static_cast<bluetooth::hci::AddressType>(
-                          properties_.GetLeAdvertisingOwnAddressType())),
-      static_cast<uint8_t>(bluetooth::hci::Role::SLAVE), connection_interval,
-      connect.GetLeConnectionLatency(),
+      my_address, static_cast<uint8_t>(bluetooth::hci::Role::SLAVE),
+      connection_interval, connect.GetLeConnectionLatency(),
       connect.GetLeConnectionSupervisionTimeout());
 
   auto to_send = model::packets::LeConnectCompleteBuilder::Create(
       incoming.GetDestinationAddress(), incoming.GetSourceAddress(),
       connection_interval, connect.GetLeConnectionLatency(),
       connect.GetLeConnectionSupervisionTimeout(),
-      properties_.GetLeAdvertisingOwnAddressType());
+      static_cast<uint8_t>(my_address.GetAddressType()));
   SendLeLinkLayerPacket(std::move(to_send));
 }
 
@@ -848,18 +955,70 @@
       complete.GetLeConnectionSupervisionTimeout());
 }
 
+void LinkLayerController::IncomingLeEncryptConnection(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_INFO();
+
+  // TODO: Check keys
+  Address peer = incoming.GetSourceAddress();
+  uint16_t handle = connections_.GetHandleOnlyAddress(peer);
+  if (handle == acl::kReservedHandle) {
+    LOG_INFO("@%s: Unknown connection @%s",
+             incoming.GetDestinationAddress().ToString().c_str(),
+             peer.ToString().c_str());
+    return;
+  }
+  ErrorCode status = ErrorCode::SUCCESS;
+  auto le_encrypt = model::packets::LeEncryptConnectionView::Create(incoming);
+  ASSERT(le_encrypt.IsValid());
+
+  if (connections_.IsEncrypted(handle)) {
+    send_event_(bluetooth::hci::EncryptionKeyRefreshCompleteBuilder::Create(
+        status, handle));
+  } else {
+    connections_.Encrypt(handle);
+    send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
+        status, handle, bluetooth::hci::EncryptionEnabled::ON));
+  }
+  SendLeLinkLayerPacket(
+      model::packets::LeEncryptConnectionResponseBuilder::Create(
+          connections_.GetOwnAddress(handle).GetAddress(), peer,
+          le_encrypt.GetRand(), le_encrypt.GetEdiv(), le_encrypt.GetLtk()));
+}
+
+void LinkLayerController::IncomingLeEncryptConnectionResponse(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_INFO();
+  // TODO: Check keys
+  uint16_t handle =
+      connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
+  if (handle == acl::kReservedHandle) {
+    LOG_INFO("@%s: Unknown connection @%s",
+             incoming.GetDestinationAddress().ToString().c_str(),
+             incoming.GetSourceAddress().ToString().c_str());
+    return;
+  }
+  ErrorCode status = ErrorCode::SUCCESS;
+
+  if (connections_.IsEncrypted(handle)) {
+    send_event_(bluetooth::hci::EncryptionKeyRefreshCompleteBuilder::Create(
+        status, handle));
+  } else {
+    connections_.Encrypt(handle);
+    send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
+        status, handle, bluetooth::hci::EncryptionEnabled::ON));
+  }
+}
+
 void LinkLayerController::IncomingLeScanPacket(
     model::packets::LinkLayerPacketView incoming) {
-  LOG_INFO("LE Scan Packet");
-
-  auto to_send = model::packets::LeScanResponseBuilder::Create(
-      properties_.GetLeAddress(), incoming.GetSourceAddress(),
-      static_cast<model::packets::AddressType>(properties_.GetLeAddressType()),
-      static_cast<model::packets::AdvertisementType>(
-          properties_.GetLeAdvertisementType()),
-      properties_.GetLeScanResponse());
-
-  SendLeLinkLayerPacket(std::move(to_send));
+  for (auto& advertiser : advertisers_) {
+    auto to_send = advertiser.GetScanResponse(incoming.GetDestinationAddress(),
+                                              incoming.GetSourceAddress());
+    if (to_send != nullptr) {
+      SendLeLinkLayerPacket(std::move(to_send));
+    }
+  }
 }
 
 void LinkLayerController::IncomingLeScanResponsePacket(
@@ -867,41 +1026,69 @@
   auto scan_response = model::packets::LeScanResponseView::Create(incoming);
   ASSERT(scan_response.IsValid());
   vector<uint8_t> ad = scan_response.GetData();
-  auto adv_type = static_cast<LeAdvertisement::AdvertisementType>(
-      scan_response.GetAdvertisementType());
+  auto adv_type = scan_response.GetAdvertisementType();
   auto address_type =
       static_cast<LeAdvertisement::AddressType>(scan_response.GetAddressType());
+  if (le_scan_enable_ == bluetooth::hci::OpCode::LE_SET_SCAN_ENABLE) {
+    if (adv_type != model::packets::AdvertisementType::SCAN_RESPONSE) {
+      return;
+    }
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>();
+    raw_builder_ptr->AddOctets1(
+        static_cast<uint8_t>(bluetooth::hci::SubeventCode::ADVERTISING_REPORT));
+    raw_builder_ptr->AddOctets1(0x01);  // num reports
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(
+        bluetooth::hci::AdvertisingEventType::SCAN_RESPONSE));
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(address_type));
+    raw_builder_ptr->AddAddress(incoming.GetSourceAddress());
+    raw_builder_ptr->AddOctets1(ad.size());
+    raw_builder_ptr->AddOctets(ad);
+    raw_builder_ptr->AddOctets1(GetRssi());
+    auto packet = bluetooth::hci::EventPacketBuilder::Create(
+        bluetooth::hci::EventCode::LE_META_EVENT, std::move(raw_builder_ptr));
+    send_event_(std::move(packet));
+  }
 
-  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-      std::make_unique<bluetooth::packet::RawBuilder>();
-  raw_builder_ptr->AddOctets1(
-      static_cast<uint8_t>(bluetooth::hci::SubeventCode::ADVERTISING_REPORT));
-  raw_builder_ptr->AddOctets1(0x01);  // num reports
-  raw_builder_ptr->AddOctets1(static_cast<uint8_t>(adv_type));
-  raw_builder_ptr->AddOctets1(static_cast<uint8_t>(address_type));
-  raw_builder_ptr->AddAddress(incoming.GetSourceAddress());
-  raw_builder_ptr->AddOctets1(ad.size());
-  raw_builder_ptr->AddOctets(ad);
-  raw_builder_ptr->AddOctets1(GetRssi());
-  auto packet = bluetooth::hci::EventPacketBuilder::Create(
-      bluetooth::hci::EventCode::LE_META_EVENT, std::move(raw_builder_ptr));
-  send_event_(std::move(packet));
+  if (le_scan_enable_ == bluetooth::hci::OpCode::LE_SET_EXTENDED_SCAN_ENABLE) {
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>();
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(
+        bluetooth::hci::SubeventCode::EXTENDED_ADVERTISING_REPORT));
+    raw_builder_ptr->AddOctets1(0x01);  // num reports
+    raw_builder_ptr->AddOctets1(0x1a);  // TODO: 0x1b for ADV_SCAN_IND
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(address_type));
+    raw_builder_ptr->AddAddress(incoming.GetSourceAddress());
+    raw_builder_ptr->AddOctets1(1);     // Primary_PHY
+    raw_builder_ptr->AddOctets1(0);     // Secondary_PHY
+    raw_builder_ptr->AddOctets1(0xFF);  // Advertising_SID - not provided
+    raw_builder_ptr->AddOctets1(0x7F);  // Tx_Power - Not available
+    raw_builder_ptr->AddOctets1(GetRssi());
+    raw_builder_ptr->AddOctets1(0);  // Periodic_Advertising_Interval - None
+    raw_builder_ptr->AddOctets1(0);  // Direct_Address_Type - PUBLIC
+    raw_builder_ptr->AddAddress(Address::kEmpty);  // Direct_Address
+    raw_builder_ptr->AddOctets1(ad.size());
+    raw_builder_ptr->AddOctets(ad);
+    auto packet = bluetooth::hci::EventPacketBuilder::Create(
+        bluetooth::hci::EventCode::LE_META_EVENT, std::move(raw_builder_ptr));
+    send_event_(std::move(packet));
+  }
 }
 
 void LinkLayerController::IncomingPagePacket(
     model::packets::LinkLayerPacketView incoming) {
   auto page = model::packets::PageView::Create(incoming);
   ASSERT(page.IsValid());
-  LOG_INFO("%s from %s", __func__, incoming.GetSourceAddress().ToString().c_str());
+  LOG_INFO("from %s", incoming.GetSourceAddress().ToString().c_str());
 
   if (!connections_.CreatePendingConnection(
           incoming.GetSourceAddress(), properties_.GetAuthenticationEnable())) {
     // Send a response to indicate that we're busy, or drop the packet?
-    LOG_WARN("%s: Failed to create a pending connection for %s", __func__,
+    LOG_WARN("Failed to create a pending connection for %s",
              incoming.GetSourceAddress().ToString().c_str());
   }
 
-  bluetooth::hci::Address source_address;
+  bluetooth::hci::Address source_address{};
   bluetooth::hci::Address::FromString(page.GetSourceAddress().ToString(),
                                       source_address);
 
@@ -914,10 +1101,10 @@
 
 void LinkLayerController::IncomingPageRejectPacket(
     model::packets::LinkLayerPacketView incoming) {
-  LOG_INFO("%s: %s", __func__, incoming.GetSourceAddress().ToString().c_str());
+  LOG_INFO("%s", incoming.GetSourceAddress().ToString().c_str());
   auto reject = model::packets::PageRejectView::Create(incoming);
   ASSERT(reject.IsValid());
-  LOG_INFO("%s: Sending CreateConnectionComplete", __func__);
+  LOG_INFO("Sending CreateConnectionComplete");
   auto packet = bluetooth::hci::ConnectionCompleteBuilder::Create(
       static_cast<ErrorCode>(reject.GetReason()), 0x0eff,
       incoming.GetSourceAddress(), bluetooth::hci::LinkType::ACL,
@@ -928,12 +1115,12 @@
 void LinkLayerController::IncomingPageResponsePacket(
     model::packets::LinkLayerPacketView incoming) {
   Address peer = incoming.GetSourceAddress();
-  LOG_INFO("%s: %s", __func__, peer.ToString().c_str());
+  LOG_INFO("%s", peer.ToString().c_str());
   bool awaiting_authentication = connections_.AuthenticatePendingConnection();
   uint16_t handle =
       connections_.CreateConnection(peer, incoming.GetDestinationAddress());
   if (handle == acl::kReservedHandle) {
-    LOG_WARN("%s: No free handles", __func__);
+    LOG_WARN("No free handles");
     return;
   }
   auto packet = bluetooth::hci::ConnectionCompleteBuilder::Create(
@@ -949,40 +1136,19 @@
 }
 
 void LinkLayerController::TimerTick() {
-  if (inquiry_state_ == Inquiry::InquiryState::INQUIRY) Inquiry();
-  if (inquiry_state_ == Inquiry::InquiryState::INQUIRY) PageScan();
+  if (inquiry_timer_task_id_ != kInvalidTaskId) Inquiry();
   LeAdvertising();
-  Connections();
 }
 
 void LinkLayerController::LeAdvertising() {
-  if (!le_advertising_enable_) {
-    return;
-  }
   steady_clock::time_point now = steady_clock::now();
-  if (duration_cast<milliseconds>(now - last_le_advertisement_) < milliseconds(200)) {
-    return;
+  for (auto& advertiser : advertisers_) {
+    auto ad = advertiser.GetAdvertisement(now);
+    if (ad == nullptr) {
+      continue;
+    }
+    SendLeLinkLayerPacket(std::move(ad));
   }
-  last_le_advertisement_ = now;
-
-  auto own_address_type = static_cast<model::packets::AddressType>(
-      properties_.GetLeAdvertisingOwnAddressType());
-  Address advertising_address = Address::kEmpty;
-  if (own_address_type == model::packets::AddressType::PUBLIC) {
-    advertising_address = properties_.GetAddress();
-  } else if (own_address_type == model::packets::AddressType::RANDOM) {
-    advertising_address = properties_.GetLeAddress();
-  }
-  ASSERT(advertising_address != Address::kEmpty);
-  auto to_send = model::packets::LeAdvertisementBuilder::Create(
-      advertising_address, Address::kEmpty, own_address_type,
-      static_cast<model::packets::AdvertisementType>(own_address_type),
-      properties_.GetLeAdvertisement());
-  SendLeLinkLayerPacket(std::move(to_send));
-}
-
-void LinkLayerController::Connections() {
-  // TODO: Keep connections alive?
 }
 
 void LinkLayerController::RegisterEventChannel(
@@ -998,7 +1164,8 @@
 }
 
 void LinkLayerController::RegisterScoChannel(
-    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
+    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+        callback) {
   send_sco_ = callback;
 }
 
@@ -1016,11 +1183,13 @@
 }
 
 void LinkLayerController::RegisterTaskScheduler(
-    std::function<AsyncTaskId(milliseconds, const TaskCallback&)> event_scheduler) {
+    std::function<AsyncTaskId(milliseconds, const TaskCallback&)>
+        event_scheduler) {
   schedule_task_ = event_scheduler;
 }
 
-AsyncTaskId LinkLayerController::ScheduleTask(milliseconds delay_ms, const TaskCallback& callback) {
+AsyncTaskId LinkLayerController::ScheduleTask(milliseconds delay_ms,
+                                              const TaskCallback& callback) {
   if (schedule_task_) {
     return schedule_task_(delay_ms, callback);
   } else {
@@ -1030,7 +1199,8 @@
 }
 
 void LinkLayerController::RegisterPeriodicTaskScheduler(
-    std::function<AsyncTaskId(milliseconds, milliseconds, const TaskCallback&)> periodic_event_scheduler) {
+    std::function<AsyncTaskId(milliseconds, milliseconds, const TaskCallback&)>
+        periodic_event_scheduler) {
   schedule_periodic_task_ = periodic_event_scheduler;
 }
 
@@ -1040,14 +1210,11 @@
   }
 }
 
-void LinkLayerController::RegisterTaskCancel(std::function<void(AsyncTaskId)> task_cancel) {
+void LinkLayerController::RegisterTaskCancel(
+    std::function<void(AsyncTaskId)> task_cancel) {
   cancel_task_ = task_cancel;
 }
 
-void LinkLayerController::AddControllerEvent(milliseconds delay, const TaskCallback& task) {
-  controller_events_.push_back(ScheduleTask(delay, task));
-}
-
 void LinkLayerController::WriteSimplePairingMode(bool enabled) {
   ASSERT_LOG(enabled, "The spec says don't disable this!");
   simple_pairing_mode_enabled_ = enabled;
@@ -1064,7 +1231,8 @@
   // AuthenticateRemoteStage2(address);
 }
 
-void LinkLayerController::AuthenticateRemoteStage1(const Address& peer, PairingType pairing_type) {
+void LinkLayerController::AuthenticateRemoteStage1(const Address& peer,
+                                                   PairingType pairing_type) {
   ASSERT(security_manager_.GetAuthenticationAddress() == peer);
   // TODO: Public key exchange first?
   switch (pairing_type) {
@@ -1088,7 +1256,8 @@
       send_event_(bluetooth::hci::UserPasskeyRequestBuilder::Create(peer));
       break;
     default:
-      LOG_ALWAYS_FATAL("Invalid PairingType %d", static_cast<int>(pairing_type));
+      LOG_ALWAYS_FATAL("Invalid PairingType %d",
+                       static_cast<int>(pairing_type));
   }
 }
 
@@ -1106,7 +1275,8 @@
   security_manager_.WriteKey(peer, key);
   security_manager_.AuthenticationRequestFinished();
 
-  ScheduleTask(milliseconds(5), [this, peer]() { AuthenticateRemoteStage2(peer); });
+  ScheduleTask(milliseconds(5),
+               [this, peer]() { AuthenticateRemoteStage2(peer); });
 
   return ErrorCode::SUCCESS;
 }
@@ -1117,20 +1287,22 @@
   // Simple pairing to get a key
   uint16_t handle = connections_.GetHandleOnlyAddress(address);
   if (handle == acl::kReservedHandle) {
-    LOG_INFO("%s: Device not connected %s", __func__, address.ToString().c_str());
+    LOG_INFO("Device not connected %s", address.ToString().c_str());
     return ErrorCode::UNKNOWN_CONNECTION;
   }
 
   security_manager_.AuthenticationRequest(address, handle);
 
-  ScheduleTask(milliseconds(5), [this, address]() { StartSimplePairing(address); });
+  ScheduleTask(milliseconds(5),
+               [this, address]() { StartSimplePairing(address); });
   return ErrorCode::SUCCESS;
 }
 
 ErrorCode LinkLayerController::IoCapabilityRequestReply(
     const Address& peer, uint8_t io_capability, uint8_t oob_data_present_flag,
     uint8_t authentication_requirements) {
-  security_manager_.SetLocalIoCapability(peer, io_capability, oob_data_present_flag, authentication_requirements);
+  security_manager_.SetLocalIoCapability(
+      peer, io_capability, oob_data_present_flag, authentication_requirements);
 
   PairingType pairing_type = security_manager_.GetSimplePairingType();
 
@@ -1142,7 +1314,7 @@
         properties_.GetAddress(), peer, io_capability, oob_data_present_flag,
         authentication_requirements));
   } else {
-    LOG_INFO("%s: Requesting remote capability", __func__);
+    LOG_INFO("Requesting remote capability");
 
     SendLinkLayerPacket(model::packets::IoCapabilityRequestBuilder::Create(
         properties_.GetAddress(), peer, io_capability, oob_data_present_flag,
@@ -1179,6 +1351,11 @@
 
   security_manager_.AuthenticationRequestFinished();
 
+  ScheduleTask(milliseconds(5), [this, peer]() {
+    send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
+        ErrorCode::SUCCESS, peer));
+  });
+
   ScheduleTask(milliseconds(5), [this, peer, key_vec]() {
     send_event_(bluetooth::hci::LinkKeyNotificationBuilder::Create(
         peer, key_vec, bluetooth::hci::KeyType::AUTHENTICATED_P256));
@@ -1194,6 +1371,12 @@
   if (security_manager_.GetAuthenticationAddress() != peer) {
     return ErrorCode::AUTHENTICATION_FAILURE;
   }
+
+  ScheduleTask(milliseconds(5), [this, peer]() {
+    send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
+        ErrorCode::AUTHENTICATION_FAILURE, peer));
+  });
+
   return ErrorCode::SUCCESS;
 }
 
@@ -1232,7 +1415,8 @@
   return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::HandleAuthenticationRequest(const Address& address, uint16_t handle) {
+void LinkLayerController::HandleAuthenticationRequest(const Address& address,
+                                                      uint16_t handle) {
   if (simple_pairing_mode_enabled_ == true) {
     security_manager_.AuthenticationRequest(address, handle);
     auto packet = bluetooth::hci::LinkKeyRequestBuilder::Create(address);
@@ -1260,8 +1444,8 @@
   return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::HandleSetConnectionEncryption(const Address& peer, uint16_t handle,
-                                                        uint8_t encryption_enable) {
+void LinkLayerController::HandleSetConnectionEncryption(
+    const Address& peer, uint16_t handle, uint8_t encryption_enable) {
   // TODO: Block ACL traffic or at least guard against it
 
   if (connections_.IsEncrypted(handle) && encryption_enable) {
@@ -1310,21 +1494,22 @@
 ErrorCode LinkLayerController::AcceptConnectionRequest(const Address& addr,
                                                        bool try_role_switch) {
   if (!connections_.HasPendingConnection(addr)) {
-    LOG_INFO("%s: No pending connection for %s", __func__, addr.ToString().c_str());
+    LOG_INFO("No pending connection for %s", addr.ToString().c_str());
     return ErrorCode::UNKNOWN_CONNECTION;
   }
 
-  LOG_INFO("%s: Accept in 200ms", __func__);
+  LOG_INFO("Accept in 200ms");
   ScheduleTask(milliseconds(200), [this, addr, try_role_switch]() {
-    LOG_INFO("%s: Accepted", __func__);
+    LOG_INFO("Accepted");
     MakeSlaveConnection(addr, try_role_switch);
   });
 
   return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::MakeSlaveConnection(const Address& addr, bool try_role_switch) {
-  LOG_INFO("%s sending page response to %s", __func__, addr.ToString().c_str());
+void LinkLayerController::MakeSlaveConnection(const Address& addr,
+                                              bool try_role_switch) {
+  LOG_INFO("Sending page response to %s", addr.ToString().c_str());
   auto to_send = model::packets::PageResponseBuilder::Create(
       properties_.GetAddress(), addr, try_role_switch);
   SendLinkLayerPacket(std::move(to_send));
@@ -1332,10 +1517,10 @@
   uint16_t handle =
       connections_.CreateConnection(addr, properties_.GetAddress());
   if (handle == acl::kReservedHandle) {
-    LOG_INFO("%s CreateConnection failed", __func__);
+    LOG_INFO("CreateConnection failed");
     return;
   }
-  LOG_INFO("%s CreateConnection returned handle 0x%x", __func__, handle);
+  LOG_INFO("CreateConnection returned handle 0x%x", handle);
   auto packet = bluetooth::hci::ConnectionCompleteBuilder::Create(
       ErrorCode::SUCCESS, handle, addr, bluetooth::hci::LinkType::ACL,
       bluetooth::hci::Enable::DISABLED);
@@ -1345,7 +1530,7 @@
 ErrorCode LinkLayerController::RejectConnectionRequest(const Address& addr,
                                                        uint8_t reason) {
   if (!connections_.HasPendingConnection(addr)) {
-    LOG_INFO("%s: No pending connection for %s", __func__, addr.ToString().c_str());
+    LOG_INFO("No pending connection for %s", addr.ToString().c_str());
     return ErrorCode::UNKNOWN_CONNECTION;
   }
 
@@ -1355,10 +1540,11 @@
   return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::RejectSlaveConnection(const Address& addr, uint8_t reason) {
+void LinkLayerController::RejectSlaveConnection(const Address& addr,
+                                                uint8_t reason) {
   auto to_send = model::packets::PageRejectBuilder::Create(
       properties_.GetAddress(), addr, reason);
-  LOG_INFO("%s sending page reject to %s (reason 0x%02hhx)", __func__,
+  LOG_INFO("Sending page reject to %s (reason 0x%02hhx)",
            addr.ToString().c_str(), reason);
   SendLinkLayerPacket(std::move(to_send));
 
@@ -1471,8 +1657,8 @@
     return ErrorCode::UNKNOWN_CONNECTION;
   }
 
-  if (sniff_max_interval < sniff_min_interval || sniff_attempt < 0x0001 || sniff_attempt > 0x7FFF ||
-      sniff_timeout > 0x7FFF) {
+  if (sniff_max_interval < sniff_min_interval || sniff_attempt < 0x0001 ||
+      sniff_attempt > 0x7FFF || sniff_timeout > 0x7FFF) {
     return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
   }
 
@@ -1520,6 +1706,19 @@
   return ErrorCode::SUCCESS;
 }
 
+ErrorCode LinkLayerController::WriteDefaultLinkPolicySettings(
+    uint16_t settings) {
+  if (settings > 7 /* Sniff + Hold + Role switch */) {
+    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  }
+  default_link_policy_settings_ = settings;
+  return ErrorCode::SUCCESS;
+}
+
+uint16_t LinkLayerController::ReadDefaultLinkPolicySettings() {
+  return default_link_policy_settings_;
+}
+
 ErrorCode LinkLayerController::FlowSpecification(
     uint16_t handle, uint8_t flow_direction, uint8_t service_type,
     uint32_t /* token_rate */, uint32_t /* token_bucket_size */,
@@ -1544,20 +1743,178 @@
   return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::LeWhiteListClear() {
-  le_white_list_.clear();
+ErrorCode LinkLayerController::SetLeExtendedAddress(uint8_t set,
+                                                    Address address) {
+  advertisers_[set].SetAddress(address);
+  return ErrorCode::SUCCESS;
 }
 
+ErrorCode LinkLayerController::SetLeExtendedAdvertisingData(
+    uint8_t set, const std::vector<uint8_t>& data) {
+  advertisers_[set].SetData(data);
+  return ErrorCode::SUCCESS;
+}
+
+ErrorCode LinkLayerController::SetLeExtendedAdvertisingParameters(
+    uint8_t set, uint16_t interval_min, uint16_t interval_max,
+    bluetooth::hci::LegacyAdvertisingProperties type,
+    bluetooth::hci::OwnAddressType own_address_type,
+    bluetooth::hci::PeerAddressType peer_address_type, Address peer,
+    bluetooth::hci::AdvertisingFilterPolicy filter_policy) {
+  model::packets::AdvertisementType ad_type;
+  switch (type) {
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_IND:
+      ad_type = model::packets::AdvertisementType::ADV_IND;
+      peer = Address::kEmpty;
+      break;
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_NONCONN_IND:
+      ad_type = model::packets::AdvertisementType::ADV_NONCONN_IND;
+      peer = Address::kEmpty;
+      break;
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_SCAN_IND:
+      ad_type = model::packets::AdvertisementType::ADV_SCAN_IND;
+      peer = Address::kEmpty;
+      break;
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_DIRECT_IND_HIGH:
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_DIRECT_IND_LOW:
+      ad_type = model::packets::AdvertisementType::ADV_DIRECT_IND;
+      break;
+  }
+  auto interval_ms =
+      static_cast<int>((interval_max + interval_min) * 0.625 / 2);
+
+  AddressWithType peer_address;
+  switch (peer_address_type) {
+    case bluetooth::hci::PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS:
+      peer_address = AddressWithType(
+          peer, bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS);
+      break;
+    case bluetooth::hci::PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS:
+      peer_address = AddressWithType(
+          peer, bluetooth::hci::AddressType::RANDOM_DEVICE_ADDRESS);
+      break;
+  }
+
+  AddressType own_address_address_type;
+  switch (own_address_type) {
+    case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS:
+      own_address_address_type =
+          bluetooth::hci::AddressType::RANDOM_DEVICE_ADDRESS;
+      break;
+    case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS:
+      own_address_address_type =
+          bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS;
+      break;
+    case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
+      own_address_address_type =
+          bluetooth::hci::AddressType::PUBLIC_IDENTITY_ADDRESS;
+      break;
+    case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
+      own_address_address_type =
+          bluetooth::hci::AddressType::RANDOM_IDENTITY_ADDRESS;
+      break;
+  }
+
+  bluetooth::hci::LeScanningFilterPolicy scanning_filter_policy;
+  switch (filter_policy) {
+    case bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES:
+      scanning_filter_policy =
+          bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL;
+      break;
+    case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN:
+      scanning_filter_policy =
+          bluetooth::hci::LeScanningFilterPolicy::CONNECT_LIST_ONLY;
+      break;
+    case bluetooth::hci::AdvertisingFilterPolicy::LISTED_CONNECT:
+      scanning_filter_policy =
+          bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY;
+      break;
+    case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN_AND_CONNECT:
+      scanning_filter_policy = bluetooth::hci::LeScanningFilterPolicy::
+          CONNECT_LIST_AND_INITIATORS_IDENTITY;
+      break;
+  }
+
+  advertisers_[set].InitializeExtended(own_address_address_type, peer_address,
+                                       scanning_filter_policy, ad_type,
+                                       std::chrono::milliseconds(interval_ms));
+
+  return ErrorCode::SUCCESS;
+}
+
+ErrorCode LinkLayerController::LeRemoveAdvertisingSet(uint8_t set) {
+  if (set >= advertisers_.size()) {
+    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  }
+  advertisers_[set].Disable();
+  return ErrorCode::SUCCESS;
+}
+
+ErrorCode LinkLayerController::LeClearAdvertisingSets() {
+  for (auto& advertiser : advertisers_) {
+    if (advertiser.IsEnabled()) {
+      return ErrorCode::COMMAND_DISALLOWED;
+    }
+  }
+  for (auto& advertiser : advertisers_) {
+    advertiser.Clear();
+  }
+  return ErrorCode::SUCCESS;
+}
+
+void LinkLayerController::LeConnectionUpdateComplete(
+    bluetooth::hci::LeConnectionUpdateView connection_update) {
+  uint16_t handle = connection_update.GetConnectionHandle();
+  ErrorCode status = ErrorCode::SUCCESS;
+  if (!connections_.HasHandle(handle)) {
+    status = ErrorCode::UNKNOWN_CONNECTION;
+  }
+  uint16_t interval_min = connection_update.GetConnIntervalMin();
+  uint16_t interval_max = connection_update.GetConnIntervalMax();
+  uint16_t latency = connection_update.GetConnLatency();
+  uint16_t supervision_timeout = connection_update.GetSupervisionTimeout();
+
+  if (interval_min < 6 || interval_max > 0xC80 || interval_min > interval_max ||
+      interval_max < interval_min || latency > 0x1F3 ||
+      supervision_timeout < 0xA || supervision_timeout > 0xC80 ||
+      // The Supervision_Timeout in milliseconds (*10) shall be larger than (1 +
+      // Connection_Latency) * Connection_Interval_Max (* 5/4) * 2
+      supervision_timeout <= ((((1 + latency) * interval_max * 10) / 4) / 10)) {
+    status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  }
+  uint16_t interval = (interval_min + interval_max) / 2;
+  send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create(
+      status, handle, interval, latency, supervision_timeout));
+}
+
+ErrorCode LinkLayerController::LeConnectionUpdate(
+    bluetooth::hci::LeConnectionUpdateView connection_update) {
+  uint16_t handle = connection_update.GetConnectionHandle();
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+
+  // This could negotiate with the remote device in the future
+  ScheduleTask(milliseconds(25), [this, connection_update]() {
+    LeConnectionUpdateComplete(connection_update);
+  });
+
+  return ErrorCode::SUCCESS;
+}
+
+void LinkLayerController::LeConnectListClear() { le_connect_list_.clear(); }
+
 void LinkLayerController::LeResolvingListClear() { le_resolving_list_.clear(); }
 
-void LinkLayerController::LeWhiteListAddDevice(Address addr, uint8_t addr_type) {
+void LinkLayerController::LeConnectListAddDevice(Address addr,
+                                                 uint8_t addr_type) {
   std::tuple<Address, uint8_t> new_tuple = std::make_tuple(addr, addr_type);
-  for (auto dev : le_white_list_) {
+  for (auto dev : le_connect_list_) {
     if (dev == new_tuple) {
       return;
     }
   }
-  le_white_list_.emplace_back(new_tuple);
+  le_connect_list_.emplace_back(new_tuple);
 }
 
 void LinkLayerController::LeResolvingListAddDevice(
@@ -1566,8 +1923,8 @@
   std::tuple<Address, uint8_t, std::array<uint8_t, kIrk_size>,
              std::array<uint8_t, kIrk_size>>
       new_tuple = std::make_tuple(addr, addr_type, peerIrk, localIrk);
-  for (size_t i = 0; i < le_white_list_.size(); i++) {
-    auto curr = le_white_list_[i];
+  for (size_t i = 0; i < le_connect_list_.size(); i++) {
+    auto curr = le_connect_list_[i];
     if (std::get<0>(curr) == addr && std::get<1>(curr) == addr_type) {
       le_resolving_list_[i] = new_tuple;
       return;
@@ -1584,13 +1941,117 @@
   LOG_INFO("mode = %d ", mode);
 }
 
-void LinkLayerController::LeWhiteListRemoveDevice(Address addr, uint8_t addr_type) {
+void LinkLayerController::HandleLeEnableEncryption(
+    uint16_t handle, std::array<uint8_t, 8> rand, uint16_t ediv,
+    std::array<uint8_t, 16> ltk) {
+  // TODO: Check keys
+  // TODO: Block ACL traffic or at least guard against it
+  if (!connections_.HasHandle(handle)) {
+    return;
+  }
+  SendLeLinkLayerPacket(model::packets::LeEncryptConnectionBuilder::Create(
+      connections_.GetOwnAddress(handle).GetAddress(),
+      connections_.GetAddress(handle).GetAddress(), rand, ediv, ltk));
+}
+
+ErrorCode LinkLayerController::LeEnableEncryption(uint16_t handle,
+                                                  std::array<uint8_t, 8> rand,
+                                                  uint16_t ediv,
+                                                  std::array<uint8_t, 16> ltk) {
+  if (!connections_.HasHandle(handle)) {
+    LOG_INFO("Unknown handle %04x", handle);
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+
+  ScheduleTask(milliseconds(5), [this, handle, rand, ediv, ltk]() {
+    HandleLeEnableEncryption(handle, rand, ediv, ltk);
+  });
+  return ErrorCode::SUCCESS;
+}
+
+ErrorCode LinkLayerController::SetLeAdvertisingEnable(
+    uint8_t le_advertising_enable) {
+  if (!le_advertising_enable) {
+    advertisers_[0].Disable();
+    return ErrorCode::SUCCESS;
+  }
+  auto interval_ms = (properties_.GetLeAdvertisingIntervalMax() +
+                      properties_.GetLeAdvertisingIntervalMin()) *
+                     0.625 / 2;
+
+  Address own_address = properties_.GetAddress();
+  if (properties_.GetLeAdvertisingOwnAddressType() ==
+          static_cast<uint8_t>(AddressType::RANDOM_DEVICE_ADDRESS) ||
+      properties_.GetLeAdvertisingOwnAddressType() ==
+          static_cast<uint8_t>(AddressType::RANDOM_IDENTITY_ADDRESS)) {
+    if (properties_.GetLeAddress().ToString() == "bb:bb:bb:ba:d0:1e" ||
+        properties_.GetLeAddress() == Address::kEmpty) {
+      return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+    }
+    own_address = properties_.GetLeAddress();
+  }
+  auto own_address_with_type = AddressWithType(
+      own_address, static_cast<bluetooth::hci::AddressType>(
+                       properties_.GetLeAdvertisingOwnAddressType()));
+
+  auto interval = std::chrono::milliseconds(static_cast<uint64_t>(interval_ms));
+  if (interval < std::chrono::milliseconds(20)) {
+    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  }
+  advertisers_[0].Initialize(
+      own_address_with_type,
+      bluetooth::hci::AddressWithType(
+          properties_.GetLeAdvertisingPeerAddress(),
+          static_cast<bluetooth::hci::AddressType>(
+              properties_.GetLeAdvertisingPeerAddressType())),
+      static_cast<bluetooth::hci::LeScanningFilterPolicy>(
+          properties_.GetLeAdvertisingFilterPolicy()),
+      static_cast<model::packets::AdvertisementType>(
+          properties_.GetLeAdvertisementType()),
+      properties_.GetLeAdvertisement(), properties_.GetLeScanResponse(),
+      interval);
+  advertisers_[0].Enable();
+  return ErrorCode::SUCCESS;
+}
+
+void LinkLayerController::LeDisableAdvertisingSets() {
+  for (auto& advertiser : advertisers_) {
+    advertiser.Disable();
+  }
+}
+
+uint8_t LinkLayerController::LeReadNumberOfSupportedAdvertisingSets() {
+  return advertisers_.size();
+}
+
+ErrorCode LinkLayerController::SetLeExtendedAdvertisingEnable(
+    bluetooth::hci::Enable enable,
+    const std::vector<bluetooth::hci::EnabledSet>& enabled_sets) {
+  for (const auto& set : enabled_sets) {
+    if (set.advertising_handle_ > advertisers_.size()) {
+      return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+    }
+  }
+  for (const auto& set : enabled_sets) {
+    auto handle = set.advertising_handle_;
+    if (enable == bluetooth::hci::Enable::ENABLED) {
+      advertisers_[handle].EnableExtended(
+          std::chrono::milliseconds(10 * set.duration_));
+    } else {
+      advertisers_[handle].Disable();
+    }
+  }
+  return ErrorCode::SUCCESS;
+}
+
+void LinkLayerController::LeConnectListRemoveDevice(Address addr,
+                                                    uint8_t addr_type) {
   // TODO: Add checks to see if advertising, scanning, or a connection request
-  // with the white list is ongoing.
+  // with the connect list is ongoing.
   std::tuple<Address, uint8_t> erase_tuple = std::make_tuple(addr, addr_type);
-  for (size_t i = 0; i < le_white_list_.size(); i++) {
-    if (le_white_list_[i] == erase_tuple) {
-      le_white_list_.erase(le_white_list_.begin() + i);
+  for (size_t i = 0; i < le_connect_list_.size(); i++) {
+    if (le_connect_list_[i] == erase_tuple) {
+      le_connect_list_.erase(le_connect_list_.begin() + i);
     }
   }
 }
@@ -1598,19 +2059,20 @@
 void LinkLayerController::LeResolvingListRemoveDevice(Address addr,
                                                       uint8_t addr_type) {
   // TODO: Add checks to see if advertising, scanning, or a connection request
-  // with the white list is ongoing.
-  for (size_t i = 0; i < le_white_list_.size(); i++) {
-    auto curr = le_white_list_[i];
+  // with the connect list is ongoing.
+  for (size_t i = 0; i < le_connect_list_.size(); i++) {
+    auto curr = le_connect_list_[i];
     if (std::get<0>(curr) == addr && std::get<1>(curr) == addr_type) {
       le_resolving_list_.erase(le_resolving_list_.begin() + i);
     }
   }
 }
 
-bool LinkLayerController::LeWhiteListContainsDevice(Address addr, uint8_t addr_type) {
+bool LinkLayerController::LeConnectListContainsDevice(Address addr,
+                                                      uint8_t addr_type) {
   std::tuple<Address, uint8_t> sought_tuple = std::make_tuple(addr, addr_type);
-  for (size_t i = 0; i < le_white_list_.size(); i++) {
-    if (le_white_list_[i] == sought_tuple) {
+  for (size_t i = 0; i < le_connect_list_.size(); i++) {
+    if (le_connect_list_[i] == sought_tuple) {
       return true;
     }
   }
@@ -1619,8 +2081,8 @@
 
 bool LinkLayerController::LeResolvingListContainsDevice(Address addr,
                                                         uint8_t addr_type) {
-  for (size_t i = 0; i < le_white_list_.size(); i++) {
-    auto curr = le_white_list_[i];
+  for (size_t i = 0; i < le_connect_list_.size(); i++) {
+    auto curr = le_connect_list_[i];
     if (std::get<0>(curr) == addr && std::get<1>(curr) == addr_type) {
       return true;
     }
@@ -1628,8 +2090,8 @@
   return false;
 }
 
-bool LinkLayerController::LeWhiteListFull() {
-  return le_white_list_.size() >= properties_.GetLeWhiteListSize();
+bool LinkLayerController::LeConnectListFull() {
+  return le_connect_list_.size() >= properties_.GetLeConnectListSize();
 }
 
 bool LinkLayerController::LeResolvingListFull() {
@@ -1637,28 +2099,31 @@
 }
 
 void LinkLayerController::Reset() {
-  inquiry_state_ = Inquiry::InquiryState::STANDBY;
+  if (inquiry_timer_task_id_ != kInvalidTaskId) {
+    CancelScheduledTask(inquiry_timer_task_id_);
+    inquiry_timer_task_id_ = kInvalidTaskId;
+  }
   last_inquiry_ = steady_clock::now();
-  le_scan_enable_ = 0;
-  le_advertising_enable_ = 0;
+  le_scan_enable_ = bluetooth::hci::OpCode::NONE;
+  LeDisableAdvertisingSets();
   le_connect_ = 0;
 }
 
-void LinkLayerController::PageScan() {}
-
 void LinkLayerController::StartInquiry(milliseconds timeout) {
-  ScheduleTask(milliseconds(timeout), [this]() { LinkLayerController::InquiryTimeout(); });
-  inquiry_state_ = Inquiry::InquiryState::INQUIRY;
+  inquiry_timer_task_id_ = ScheduleTask(milliseconds(timeout), [this]() {
+    LinkLayerController::InquiryTimeout();
+  });
 }
 
 void LinkLayerController::InquiryCancel() {
-  ASSERT(inquiry_state_ == Inquiry::InquiryState::INQUIRY);
-  inquiry_state_ = Inquiry::InquiryState::STANDBY;
+  ASSERT(inquiry_timer_task_id_ != kInvalidTaskId);
+  CancelScheduledTask(inquiry_timer_task_id_);
+  inquiry_timer_task_id_ = kInvalidTaskId;
 }
 
 void LinkLayerController::InquiryTimeout() {
-  if (inquiry_state_ == Inquiry::InquiryState::INQUIRY) {
-    inquiry_state_ = Inquiry::InquiryState::STANDBY;
+  if (inquiry_timer_task_id_ != kInvalidTaskId) {
+    inquiry_timer_task_id_ = kInvalidTaskId;
     auto packet =
         bluetooth::hci::InquiryCompleteBuilder::Create(ErrorCode::SUCCESS);
     send_event_(std::move(packet));
@@ -1669,9 +2134,7 @@
   inquiry_mode_ = static_cast<model::packets::InquiryType>(mode);
 }
 
-void LinkLayerController::SetInquiryLAP(uint64_t lap) {
-  inquiry_lap_ = lap;
-}
+void LinkLayerController::SetInquiryLAP(uint64_t lap) { inquiry_lap_ = lap; }
 
 void LinkLayerController::SetInquiryMaxResponses(uint8_t max) {
   inquiry_max_responses_ = max;
diff --git a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
index 79027ec..e572f4f 100644
--- a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
+++ b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
@@ -16,12 +16,13 @@
 
 #pragma once
 
-#include "acl_connection_handler.h"
 #include "hci/address.h"
 #include "hci/hci_packets.h"
 #include "include/hci.h"
 #include "include/inquiry.h"
 #include "include/phy.h"
+#include "model/controller/acl_connection_handler.h"
+#include "model/controller/le_advertiser.h"
 #include "model/devices/device_properties.h"
 #include "model/setup/async_manager.h"
 #include "packets/link_layer_packets.h"
@@ -124,23 +125,37 @@
 
   void RegisterTaskCancel(std::function<void(AsyncTaskId)> cancel);
   void Reset();
-  void AddControllerEvent(std::chrono::milliseconds delay, const TaskCallback& task);
-
-  void PageScan();
-  void Connections();
 
   void LeAdvertising();
 
+  ErrorCode SetLeExtendedAddress(uint8_t handle, Address address);
+
+  ErrorCode SetLeExtendedAdvertisingData(uint8_t handle,
+                                         const std::vector<uint8_t>& data);
+
+  ErrorCode SetLeExtendedAdvertisingParameters(
+      uint8_t set, uint16_t interval_min, uint16_t interval_max,
+      bluetooth::hci::LegacyAdvertisingProperties type,
+      bluetooth::hci::OwnAddressType own_address_type,
+      bluetooth::hci::PeerAddressType peer_address_type, Address peer,
+      bluetooth::hci::AdvertisingFilterPolicy filter_policy);
+  ErrorCode LeRemoveAdvertisingSet(uint8_t set);
+  ErrorCode LeClearAdvertisingSets();
+  void LeConnectionUpdateComplete(
+      bluetooth::hci::LeConnectionUpdateView connection_update_view);
+  ErrorCode LeConnectionUpdate(
+      bluetooth::hci::LeConnectionUpdateView connection_update_view);
+
   void HandleLeConnection(AddressWithType addr, AddressWithType own_addr,
                           uint8_t role, uint16_t connection_interval,
                           uint16_t connection_latency,
                           uint16_t supervision_timeout);
 
-  void LeWhiteListClear();
-  void LeWhiteListAddDevice(Address addr, uint8_t addr_type);
-  void LeWhiteListRemoveDevice(Address addr, uint8_t addr_type);
-  bool LeWhiteListContainsDevice(Address addr, uint8_t addr_type);
-  bool LeWhiteListFull();
+  void LeConnectListClear();
+  void LeConnectListAddDevice(Address addr, uint8_t addr_type);
+  void LeConnectListRemoveDevice(Address addr, uint8_t addr_type);
+  bool LeConnectListContainsDevice(Address addr, uint8_t addr_type);
+  bool LeConnectListFull();
   void LeResolvingListClear();
   void LeResolvingListAddDevice(Address addr, uint8_t addr_type,
                                 std::array<uint8_t, kIrk_size> peerIrk,
@@ -150,14 +165,24 @@
   bool LeResolvingListFull();
   void LeSetPrivacyMode(uint8_t address_type, Address addr, uint8_t mode);
 
-  ErrorCode SetLeAdvertisingEnable(uint8_t le_advertising_enable) {
-    le_advertising_enable_ = le_advertising_enable;
-    // TODO: Check properties and return errors
-    return ErrorCode::SUCCESS;
-  }
+  void HandleLeEnableEncryption(uint16_t handle, std::array<uint8_t, 8> rand,
+                                uint16_t ediv, std::array<uint8_t, 16> ltk);
 
-  void SetLeScanEnable(uint8_t le_scan_enable) {
-    le_scan_enable_ = le_scan_enable;
+  ErrorCode LeEnableEncryption(uint16_t handle, std::array<uint8_t, 8> rand,
+                               uint16_t ediv, std::array<uint8_t, 16> ltk);
+
+  ErrorCode SetLeAdvertisingEnable(uint8_t le_advertising_enable);
+
+  void LeDisableAdvertisingSets();
+
+  uint8_t LeReadNumberOfSupportedAdvertisingSets();
+
+  ErrorCode SetLeExtendedAdvertisingEnable(
+      bluetooth::hci::Enable enable,
+      const std::vector<bluetooth::hci::EnabledSet>& enabled_sets);
+
+  void SetLeScanEnable(bluetooth::hci::OpCode enabling_opcode) {
+    le_scan_enable_ = enabling_opcode;
   }
   void SetLeScanType(uint8_t le_scan_type) {
     le_scan_type_ = le_scan_type;
@@ -240,6 +265,8 @@
                               uint32_t token_bucket_size,
                               uint32_t peak_bandwidth, uint32_t access_latency);
   ErrorCode WriteLinkSupervisionTimeout(uint16_t handle, uint16_t timeout);
+  ErrorCode WriteDefaultLinkPolicySettings(uint16_t settings);
+  uint16_t ReadDefaultLinkPolicySettings();
 
  protected:
   void SendLeLinkLayerPacket(
@@ -247,9 +274,6 @@
   void SendLinkLayerPacket(
       std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet);
   void IncomingAclPacket(model::packets::LinkLayerPacketView packet);
-  void IncomingAclAckPacket(model::packets::LinkLayerPacketView packet);
-  void IncomingCreateConnectionPacket(
-      model::packets::LinkLayerPacketView packet);
   void IncomingDisconnectPacket(model::packets::LinkLayerPacketView packet);
   void IncomingEncryptConnection(model::packets::LinkLayerPacketView packet);
   void IncomingEncryptConnectionResponse(
@@ -268,6 +292,9 @@
   void IncomingLeConnectPacket(model::packets::LinkLayerPacketView packet);
   void IncomingLeConnectCompletePacket(
       model::packets::LinkLayerPacketView packet);
+  void IncomingLeEncryptConnection(model::packets::LinkLayerPacketView packet);
+  void IncomingLeEncryptConnectionResponse(
+      model::packets::LinkLayerPacketView packet);
   void IncomingLeScanPacket(model::packets::LinkLayerPacketView packet);
   void IncomingLeScanResponsePacket(model::packets::LinkLayerPacketView packet);
   void IncomingPagePacket(model::packets::LinkLayerPacketView packet);
@@ -304,7 +331,7 @@
 
   // Timing related state
   std::vector<AsyncTaskId> controller_events_;
-  AsyncTaskId timer_tick_task_;
+  AsyncTaskId timer_tick_task_{};
   std::chrono::milliseconds timer_period_ = std::chrono::milliseconds(100);
 
   // Callbacks to schedule tasks.
@@ -329,42 +356,43 @@
   // LE state
   std::vector<uint8_t> le_event_mask_;
 
-  std::vector<std::tuple<Address, uint8_t>> le_white_list_;
+  std::vector<std::tuple<Address, uint8_t>> le_connect_list_;
   std::vector<std::tuple<Address, uint8_t, std::array<uint8_t, kIrk_size>,
                          std::array<uint8_t, kIrk_size>>>
       le_resolving_list_;
 
-  uint8_t le_advertising_enable_{false};
-  std::chrono::steady_clock::time_point last_le_advertisement_;
+  std::array<LeAdvertiser, 3> advertisers_;
 
-  uint8_t le_scan_enable_{false};
-  uint8_t le_scan_type_;
-  uint16_t le_scan_interval_;
-  uint16_t le_scan_window_;
-  uint8_t le_scan_filter_policy_;
-  uint8_t le_scan_filter_duplicates_;
-  uint8_t le_address_type_;
+  bluetooth::hci::OpCode le_scan_enable_{bluetooth::hci::OpCode::NONE};
+  uint8_t le_scan_type_{};
+  uint16_t le_scan_interval_{};
+  uint16_t le_scan_window_{};
+  uint8_t le_scan_filter_policy_{};
+  uint8_t le_scan_filter_duplicates_{};
+  uint8_t le_address_type_{};
 
   bool le_connect_{false};
-  uint16_t le_connection_interval_min_;
-  uint16_t le_connection_interval_max_;
-  uint16_t le_connection_latency_;
-  uint16_t le_connection_supervision_timeout_;
-  uint16_t le_connection_minimum_ce_length_;
-  uint16_t le_connection_maximum_ce_length_;
-  uint8_t le_initiator_filter_policy_;
+  uint16_t le_connection_interval_min_{};
+  uint16_t le_connection_interval_max_{};
+  uint16_t le_connection_latency_{};
+  uint16_t le_connection_supervision_timeout_{};
+  uint16_t le_connection_minimum_ce_length_{};
+  uint16_t le_connection_maximum_ce_length_{};
+  uint8_t le_initiator_filter_policy_{};
 
-  Address le_peer_address_;
-  uint8_t le_peer_address_type_;
+  Address le_peer_address_{};
+  uint8_t le_peer_address_type_{};
 
   // Classic state
 
   SecurityManager security_manager_{10};
   std::chrono::steady_clock::time_point last_inquiry_;
-  model::packets::InquiryType inquiry_mode_;
-  Inquiry::InquiryState inquiry_state_;
-  uint64_t inquiry_lap_;
-  uint8_t inquiry_max_responses_;
+  model::packets::InquiryType inquiry_mode_{
+      model::packets::InquiryType::STANDARD};
+  AsyncTaskId inquiry_timer_task_id_ = kInvalidTaskId;
+  uint64_t inquiry_lap_{};
+  uint8_t inquiry_max_responses_{};
+  uint16_t default_link_policy_settings_ = 0;
 
   bool page_scans_enabled_{false};
   bool inquiry_scans_enabled_{false};
diff --git a/vendor_libs/test_vendor_lib/model/controller/security_manager.h b/vendor_libs/test_vendor_lib/model/controller/security_manager.h
index e77f1d1..f80f42e 100644
--- a/vendor_libs/test_vendor_lib/model/controller/security_manager.h
+++ b/vendor_libs/test_vendor_lib/model/controller/security_manager.h
@@ -90,18 +90,20 @@
   std::unordered_map<std::string, std::array<uint8_t, 16>> key_store_;
 
   bool peer_capabilities_valid_{false};
-  IoCapabilityType peer_io_capability_;
+  IoCapabilityType peer_io_capability_{IoCapabilityType::DISPLAY_ONLY};
   bool peer_oob_present_flag_{false};
-  AuthenticationType peer_authentication_requirements_;
+  AuthenticationType peer_authentication_requirements_{
+      AuthenticationType::NO_BONDING};
 
   bool host_capabilities_valid_{false};
-  IoCapabilityType host_io_capability_;
+  IoCapabilityType host_io_capability_{IoCapabilityType::DISPLAY_ONLY};
   bool host_oob_present_flag_{false};
-  AuthenticationType host_authentication_requirements_;
+  AuthenticationType host_authentication_requirements_{
+      AuthenticationType::NO_BONDING};
 
   bool authenticating_{false};
-  uint16_t current_handle_;
-  Address peer_address_;
+  uint16_t current_handle_{};
+  Address peer_address_{};
 };
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/beacon.cc b/vendor_libs/test_vendor_lib/model/devices/beacon.cc
index 96f6099..4a252bf 100644
--- a/vendor_libs/test_vendor_lib/model/devices/beacon.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/beacon.cc
@@ -50,7 +50,7 @@
 void Beacon::Initialize(const vector<std::string>& args) {
   if (args.size() < 2) return;
 
-  Address addr;
+  Address addr{};
   if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr);
 
   if (args.size() < 3) return;
@@ -70,7 +70,7 @@
     std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
         std::move(ad);
 
-    for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
+    for (const auto& phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
       phy->Send(to_send);
     }
   }
@@ -87,7 +87,7 @@
     std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
         std::move(scan_response);
 
-    for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
+    for (const auto& phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
       phy->Send(to_send);
     }
   }
diff --git a/vendor_libs/test_vendor_lib/model/devices/beacon_swarm.cc b/vendor_libs/test_vendor_lib/model/devices/beacon_swarm.cc
index c9f17f1..17e62ad 100644
--- a/vendor_libs/test_vendor_lib/model/devices/beacon_swarm.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/beacon_swarm.cc
@@ -61,7 +61,7 @@
 void BeaconSwarm::Initialize(const vector<std::string>& args) {
   if (args.size() < 2) return;
 
-  Address addr;
+  Address addr{};
   if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr);
 
   if (args.size() < 3) return;
diff --git a/vendor_libs/test_vendor_lib/model/devices/broken_adv.cc b/vendor_libs/test_vendor_lib/model/devices/broken_adv.cc
index 32c5ebb..27dc2d1 100644
--- a/vendor_libs/test_vendor_lib/model/devices/broken_adv.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/broken_adv.cc
@@ -51,7 +51,7 @@
 void BrokenAdv::Initialize(const vector<std::string>& args) {
   if (args.size() < 2) return;
 
-  Address addr;
+  Address addr{};
   if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr);
 
   if (args.size() < 3) return;
diff --git a/vendor_libs/test_vendor_lib/model/devices/car_kit.cc b/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
index 65603f1..798a760 100644
--- a/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
@@ -80,7 +80,7 @@
 void CarKit::Initialize(const vector<std::string>& args) {
   if (args.size() < 2) return;
 
-  Address addr;
+  Address addr{};
   if (Address::FromString(args[1], addr)) properties_.SetAddress(addr);
   LOG_INFO("%s SetAddress %s", ToString().c_str(), addr.ToString().c_str());
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/classic.cc b/vendor_libs/test_vendor_lib/model/devices/classic.cc
index e8df43a..def9517 100644
--- a/vendor_libs/test_vendor_lib/model/devices/classic.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/classic.cc
@@ -40,7 +40,7 @@
 void Classic::Initialize(const vector<std::string>& args) {
   if (args.size() < 2) return;
 
-  Address addr;
+  Address addr{};
   if (Address::FromString(args[1], addr)) properties_.SetAddress(addr);
 
   if (args.size() < 3) return;
diff --git a/vendor_libs/test_vendor_lib/model/devices/device.cc b/vendor_libs/test_vendor_lib/model/devices/device.cc
index 9fdcd5e..e6c7d41 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/device.cc
@@ -58,14 +58,14 @@
 void Device::SendLinkLayerPacket(
     std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send,
     Phy::Type phy_type) {
-  for (auto phy : phy_layers_[phy_type]) {
+  for (const auto& phy : phy_layers_[phy_type]) {
     phy->Send(to_send);
   }
 }
 
 void Device::SendLinkLayerPacket(model::packets::LinkLayerPacketView to_send,
                                  Phy::Type phy_type) {
-  for (auto phy : phy_layers_[phy_type]) {
+  for (const auto& phy : phy_layers_[phy_type]) {
     phy->Send(to_send);
   }
 }
diff --git a/vendor_libs/test_vendor_lib/model/devices/device.h b/vendor_libs/test_vendor_lib/model/devices/device.h
index b985888..c13a279 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device.h
+++ b/vendor_libs/test_vendor_lib/model/devices/device.h
@@ -92,11 +92,11 @@
   std::chrono::steady_clock::time_point last_advertisement_;
 
   // The time between page scans.
-  std::chrono::milliseconds page_scan_delay_ms_;
+  std::chrono::milliseconds page_scan_delay_ms_{};
 
   // The spec defines the advertising interval as a 16-bit value, but since it
   // is never sent in packets, we use std::chrono::milliseconds.
-  std::chrono::milliseconds advertising_interval_ms_;
+  std::chrono::milliseconds advertising_interval_ms_{};
 
   DeviceProperties properties_;
 };
diff --git a/vendor_libs/test_vendor_lib/model/devices/device_properties.cc b/vendor_libs/test_vendor_lib/model/devices/device_properties.cc
index 8309268..ab64f06 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device_properties.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/device_properties.cc
@@ -55,7 +55,7 @@
       lmp_pal_subversion_(0),
       le_data_packet_length_(27),
       num_le_data_packets_(15),
-      le_white_list_size_(15),
+      le_connect_list_size_(15),
       le_resolving_list_size_(15) {
   std::string properties_raw;
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/device_properties.h b/vendor_libs/test_vendor_lib/model/devices/device_properties.h
index 960e5d0..f41ebb3 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device_properties.h
+++ b/vendor_libs/test_vendor_lib/model/devices/device_properties.h
@@ -279,9 +279,7 @@
   }
 
   // Specification Version 4.2, Volume 2, Part E, Section 7.8.14
-  uint8_t GetLeWhiteListSize() const {
-    return le_white_list_size_;
-  }
+  uint8_t GetLeConnectListSize() const { return le_connect_list_size_; }
 
   // Specification Version 4.2, Volume 2, Part E, Section 7.8.27
   uint64_t GetLeSupportedStates() const {
@@ -309,39 +307,39 @@
   uint8_t lmp_pal_version_;
   uint16_t manufacturer_name_;
   uint16_t lmp_pal_subversion_;
-  uint64_t supported_features_;
-  uint8_t authentication_enable_;
+  uint64_t supported_features_{};
+  uint8_t authentication_enable_{};
   std::vector<uint8_t> supported_codecs_;
   std::vector<uint32_t> vendor_specific_codecs_;
   std::vector<uint8_t> supported_commands_;
   std::vector<uint64_t> extended_features_{{0x875b3fd8fe8ffeff, 0x0f}};
   ClassOfDevice class_of_device_{{0, 0, 0}};
   std::vector<uint8_t> extended_inquiry_data_;
-  std::array<uint8_t, 248> name_;
-  Address address_;
-  uint8_t page_scan_repetition_mode_;
-  uint16_t clock_offset_;
+  std::array<uint8_t, 248> name_{};
+  Address address_{};
+  uint8_t page_scan_repetition_mode_{};
+  uint16_t clock_offset_{};
   uint8_t encryption_key_size_{10};
 
   // Low Energy
   uint16_t le_data_packet_length_;
   uint8_t num_le_data_packets_;
-  uint8_t le_white_list_size_;
+  uint8_t le_connect_list_size_;
   uint8_t le_resolving_list_size_;
   uint64_t le_supported_features_{0x075b3fd8fe8ffeff};
   uint64_t le_supported_states_;
   std::vector<uint8_t> le_vendor_cap_;
-  Address le_address_;
-  uint8_t le_address_type_;
+  Address le_address_{};
+  uint8_t le_address_type_{};
 
-  uint16_t le_advertising_interval_min_;
-  uint16_t le_advertising_interval_max_;
-  uint8_t le_advertising_own_address_type_;
-  uint8_t le_advertising_peer_address_type_;
-  Address le_advertising_peer_address_;
-  uint8_t le_advertising_channel_map_;
-  uint8_t le_advertising_filter_policy_;
-  uint8_t le_advertisement_type_;
+  uint16_t le_advertising_interval_min_{};
+  uint16_t le_advertising_interval_max_{};
+  uint8_t le_advertising_own_address_type_{};
+  uint8_t le_advertising_peer_address_type_{};
+  Address le_advertising_peer_address_{};
+  uint8_t le_advertising_channel_map_{};
+  uint8_t le_advertising_filter_policy_{};
+  uint8_t le_advertisement_type_{};
   std::vector<uint8_t> le_advertisement_;
   std::vector<uint8_t> le_scan_response_;
 };
diff --git a/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.h b/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.h
index c8b7917..99b2b00 100644
--- a/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.h
+++ b/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.h
@@ -74,7 +74,7 @@
 
   enum State { HCI_PREAMBLE, HCI_PAYLOAD };
   State state_{HCI_PREAMBLE};
-  uint8_t preamble_[PREAMBLE_SIZE_MAX];
+  uint8_t preamble_[PREAMBLE_SIZE_MAX]{};
   std::vector<uint8_t> packet_;
   size_t bytes_remaining_{0};
   size_t bytes_read_{0};
diff --git a/vendor_libs/test_vendor_lib/model/devices/hci_packetizer.h b/vendor_libs/test_vendor_lib/model/devices/hci_packetizer.h
index 4823008..e31c65a 100644
--- a/vendor_libs/test_vendor_lib/model/devices/hci_packetizer.h
+++ b/vendor_libs/test_vendor_lib/model/devices/hci_packetizer.h
@@ -57,7 +57,7 @@
  protected:
   enum State { HCI_PREAMBLE, HCI_PAYLOAD };
   State state_{HCI_PREAMBLE};
-  uint8_t preamble_[PREAMBLE_SIZE_MAX];
+  uint8_t preamble_[PREAMBLE_SIZE_MAX]{};
   std::vector<uint8_t> packet_;
   size_t bytes_remaining_{0};
   size_t bytes_read_{0};
diff --git a/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.cc b/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.cc
index 6865714..1c592bc 100644
--- a/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.cc
@@ -88,6 +88,7 @@
       },
       [this]() {
         LOG_INFO("HCI socket device disconnected");
+        socket_file_descriptor_ = -1;
         close_callback_();
       });
 
@@ -105,7 +106,8 @@
 
 void HciSocketDevice::SendHci(hci::PacketType packet_type, const std::shared_ptr<std::vector<uint8_t>> packet) {
   if (socket_file_descriptor_ == -1) {
-    LOG_INFO("socket_file_descriptor == -1");
+    LOG_INFO("Closed socket. Dropping packet of type %d",
+             static_cast<int>(packet_type));
     return;
   }
   uint8_t type = static_cast<uint8_t>(packet_type);
@@ -121,7 +123,9 @@
 }
 
 void HciSocketDevice::RegisterCloseCallback(std::function<void()> close_callback) {
-  close_callback_ = close_callback;
+  if (socket_file_descriptor_ != -1) {
+    close_callback_ = close_callback;
+  }
 }
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/keyboard.cc b/vendor_libs/test_vendor_lib/model/devices/keyboard.cc
index 1244d3e..f089704 100644
--- a/vendor_libs/test_vendor_lib/model/devices/keyboard.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/keyboard.cc
@@ -66,7 +66,7 @@
 void Keyboard::Initialize(const vector<std::string>& args) {
   if (args.size() < 2) return;
 
-  Address addr;
+  Address addr{};
   if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr);
 
   if (args.size() < 3) return;
diff --git a/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.h b/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.h
index 564233c..4c03200 100644
--- a/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.h
+++ b/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.h
@@ -53,7 +53,7 @@
   net::PolledSocket socket_;
   Phy::Type phy_type_;
   size_t bytes_left_{0};
-  size_t offset_;
+  size_t offset_{};
   std::shared_ptr<std::vector<uint8_t>> received_;
   std::vector<model::packets::LinkLayerPacketView> packet_queue_;
 };
diff --git a/vendor_libs/test_vendor_lib/model/devices/loopback.cc b/vendor_libs/test_vendor_lib/model/devices/loopback.cc
index df0c762..30a8612 100644
--- a/vendor_libs/test_vendor_lib/model/devices/loopback.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/loopback.cc
@@ -56,7 +56,7 @@
 void Loopback::Initialize(const vector<std::string>& args) {
   if (args.size() < 2) return;
 
-  Address addr;
+  Address addr{};
   if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr);
 
   if (args.size() < 3) return;
@@ -80,7 +80,7 @@
     std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
         std::move(scan_response);
 
-    for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
+    for (const auto& phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
       LOG_INFO("Sending a Scan Response on a Phy");
       phy->Send(to_send);
     }
diff --git a/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.cc b/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.cc
index db87028..60f38aa 100644
--- a/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.cc
@@ -38,7 +38,7 @@
 void RemoteLoopbackDevice::Initialize(const std::vector<std::string>& args) {
   if (args.size() < 2) return;
 
-  Address addr;
+  Address addr{};
   if (Address::FromString(args[1], addr)) properties_.SetAddress(addr);
 }
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc
new file mode 100644
index 0000000..ffbdd8c
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "scripted_beacon.h"
+
+#include <fstream>
+#include <cstdint>
+#include <unistd.h>
+
+#include "model/devices/scripted_beacon_ble_payload.pb.h"
+#include "model/setup/device_boutique.h"
+#include "os/log.h"
+
+using std::vector;
+using std::chrono::steady_clock;
+using std::chrono::system_clock;
+
+namespace test_vendor_lib {
+bool ScriptedBeacon::registered_ =
+    DeviceBoutique::Register("scripted_beacon", &ScriptedBeacon::Create);
+ScriptedBeacon::ScriptedBeacon() {
+  advertising_interval_ms_ = std::chrono::milliseconds(1280);
+  properties_.SetLeAdvertisementType(0x02 /* SCANNABLE */);
+  properties_.SetLeAdvertisement({
+      0x18,  // Length
+      0x09 /* TYPE_NAME_CMPL */,
+      'g',
+      'D',
+      'e',
+      'v',
+      'i',
+      'c',
+      'e',
+      '-',
+      's',
+      'c',
+      'r',
+      'i',
+      'p',
+      't',
+      'e',
+      'd',
+      '-',
+      'b',
+      'e',
+      'a',
+      'c',
+      'o',
+      'n',
+      0x02,  // Length
+      0x01 /* TYPE_FLAG */,
+      0x4 /* BREDR_NOT_SPT */ | 0x2 /* GEN_DISC_FLAG */,
+  });
+
+  properties_.SetLeScanResponse({0x05,  // Length
+                                 0x08,  // TYPE_NAME_SHORT
+                                 'g', 'b', 'e', 'a'});
+  LOG_INFO("Scripted_beacon registered %s", registered_ ? "true" : "false");
+}
+
+bool has_time_elapsed(steady_clock::time_point time_point) {
+  return steady_clock::now() > time_point;
+}
+
+void ScriptedBeacon::Initialize(const vector<std::string>& args) {
+  if (args.size() < 2) {
+    LOG_ERROR(
+        "Initialization failed, need mac address, playback and playback events "
+        "file arguments");
+    return;
+  }
+
+  Address addr{};
+  if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr);
+
+  if (args.size() < 4) {
+    LOG_ERROR(
+        "Initialization failed, need playback and playback events file "
+        "arguments");
+  }
+  config_file_ = args[2];
+  events_file_ = args[3];
+  set_state(PlaybackEvent::INITIALIZED);
+}
+
+void ScriptedBeacon::populate_event(PlaybackEvent * event, PlaybackEvent::PlaybackEventType type) {
+  LOG_INFO("Adding event: %d", type);
+  event->set_type(type);
+  event->set_secs_since_epoch(system_clock::now().time_since_epoch().count());
+}
+
+// Adds events to events file; we won't be able to post anything to the file
+// until we set to permissive mode in tests. No events are posted until then.
+void ScriptedBeacon::set_state(PlaybackEvent::PlaybackEventType state) {
+  PlaybackEvent event;
+  current_state_ = state;
+  if (!events_ostream_.is_open()) {
+    events_ostream_.open(events_file_, std::ios::out | std::ios::binary | std::ios::trunc);
+    if (!events_ostream_.is_open()) {
+      LOG_INFO("Events file not opened yet, for event: %d", state);
+      return;
+    }
+  }
+  populate_event(&event, state);
+  event.SerializeToOstream(&events_ostream_);
+  events_ostream_.flush();
+}
+
+void ScriptedBeacon::TimerTick() {
+  switch (current_state_) {
+    case PlaybackEvent::INITIALIZED:
+      Beacon::TimerTick();
+      break;
+    case PlaybackEvent::SCANNED_ONCE:
+      next_check_time_ =
+          steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
+      set_state(PlaybackEvent::WAITING_FOR_FILE);
+      break;
+    case PlaybackEvent::WAITING_FOR_FILE:
+      if (!has_time_elapsed(next_check_time_)) {
+        return;
+      }
+      next_check_time_ =
+          steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
+      if (access(config_file_.c_str(), F_OK) == -1) {
+        return;
+      }
+      set_state(PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE);
+      break;
+    case PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE:
+      if (access(config_file_.c_str(), R_OK) == -1) {
+        return;
+      }
+      set_state(PlaybackEvent::PARSING_FILE);
+      break;
+    case PlaybackEvent::PARSING_FILE: {
+      if (!has_time_elapsed(next_check_time_)) {
+        return;
+      }
+      std::fstream input(config_file_, std::ios::in | std::ios::binary);
+      if (!ble_ad_list_.ParseFromIstream(&input)) {
+        LOG_ERROR("Cannot parse playback file %s", config_file_.c_str());
+        set_state(PlaybackEvent::FILE_PARSING_FAILED);
+        return;
+      } else {
+        set_state(PlaybackEvent::PLAYBACK_STARTED);
+        LOG_INFO("Starting Ble advertisement playback from file: %s",
+                 config_file_.c_str());
+        next_ad_.ad_time = steady_clock::now();
+        get_next_advertisement();
+        input.close();
+      }
+    } break;
+    case PlaybackEvent::PLAYBACK_STARTED: {
+      std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send;
+      while (has_time_elapsed(next_ad_.ad_time)) {
+        auto ad = model::packets::LeAdvertisementBuilder::Create(
+            next_ad_.address, Address::kEmpty /* Destination */,
+            model::packets::AddressType::RANDOM,
+            model::packets::AdvertisementType::ADV_NONCONN_IND, next_ad_.ad);
+        to_send = std::move(ad);
+        for (const auto& phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
+          phy->Send(to_send);
+        }
+        if (packet_num_ < ble_ad_list_.advertisements().size()) {
+          get_next_advertisement();
+        } else {
+          set_state(PlaybackEvent::PLAYBACK_ENDED);
+          if (events_ostream_.is_open()) {
+            events_ostream_.close();
+          }
+          LOG_INFO(
+              "Completed Ble advertisement playback from file: %s with %d "
+              "packets",
+              config_file_.c_str(), packet_num_);
+          break;
+        }
+      }
+    } break;
+    case PlaybackEvent::FILE_PARSING_FAILED:
+    case PlaybackEvent::PLAYBACK_ENDED:
+    case PlaybackEvent::UNKNOWN:
+      return;
+  }
+}
+
+void ScriptedBeacon::IncomingPacket(
+    model::packets::LinkLayerPacketView packet) {
+  if (current_state_ == PlaybackEvent::INITIALIZED) {
+    if (packet.GetDestinationAddress() == properties_.GetLeAddress() &&
+        packet.GetType() == model::packets::PacketType::LE_SCAN) {
+      auto scan_response = model::packets::LeScanResponseBuilder::Create(
+          properties_.GetLeAddress(), packet.GetSourceAddress(),
+          static_cast<model::packets::AddressType>(
+              properties_.GetLeAddressType()),
+          model::packets::AdvertisementType::SCAN_RESPONSE,
+          properties_.GetLeScanResponse());
+      std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
+          std::move(scan_response);
+      set_state(PlaybackEvent::SCANNED_ONCE);
+      for (const auto& phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
+        phy->Send(to_send);
+      }
+    }
+  }
+}
+
+void ScriptedBeacon::get_next_advertisement() {
+  std::string payload = ble_ad_list_.advertisements(packet_num_).payload();
+  std::string mac_address =
+      ble_ad_list_.advertisements(packet_num_).mac_address();
+  uint32_t delay_before_send_ms =
+      ble_ad_list_.advertisements(packet_num_).delay_before_send_ms();
+  next_ad_.ad.assign(payload.begin(), payload.end());
+  if (Address::IsValidAddress(mac_address)) {
+    // formatted string with colons like "12:34:56:78:9a:bc"
+    Address::FromString(mac_address, next_ad_.address);
+  } else if (mac_address.size() == Address::kLength) {
+    // six-byte binary address
+    std::vector<uint8_t> mac_vector(mac_address.cbegin(), mac_address.cend());
+    next_ad_.address.Address::FromOctets(mac_vector.data());
+  } else {
+    Address::FromString("BA:D0:AD:BA:D0:AD", next_ad_.address);
+  }
+  next_ad_.ad_time +=
+      steady_clock::duration(std::chrono::milliseconds(delay_before_send_ms));
+  packet_num_++;
+}
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h
new file mode 100644
index 0000000..05406d0
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <fstream>
+
+#include "model/devices/scripted_beacon_ble_payload.pb.h"
+#include "beacon.h"
+
+using android::bluetooth::test_vendor_lib::model::devices::ScriptedBeaconBleAdProto::PlaybackEvent;
+
+namespace test_vendor_lib {
+// Pretend to be a lot of beacons by advertising from a file.
+class ScriptedBeacon : public Beacon {
+ public:
+  ScriptedBeacon();
+  virtual ~ScriptedBeacon() = default;
+
+  static std::shared_ptr<Device> Create() {
+    return std::make_shared<ScriptedBeacon>();
+  }
+
+  // Return a string representation of the type of device.
+  virtual std::string GetTypeString() const override {
+    return "scripted_beacon";
+  }
+
+  virtual std::string ToString() const override {
+    return "scripted_beacon " + config_file_;
+  }
+
+  // Set the address and advertising interval from string args.
+  void Initialize(const std::vector<std::string>& args) override;
+
+  void TimerTick() override;
+
+  void IncomingPacket(model::packets::LinkLayerPacketView packet_view) override;
+
+ private:
+  static bool registered_;
+  std::string config_file_{};
+  std::string events_file_{};
+  std::ofstream events_ostream_;
+  struct Advertisement {
+    std::vector<uint8_t> ad;
+    Address address;
+    std::chrono::steady_clock::time_point ad_time;
+  };
+
+  void populate_event(PlaybackEvent * event, PlaybackEvent::PlaybackEventType type);
+
+  void get_next_advertisement();
+
+  void set_state(
+      android::bluetooth::test_vendor_lib::model::devices::
+          ScriptedBeaconBleAdProto::PlaybackEvent::PlaybackEventType type);
+
+  Advertisement next_ad_{};
+  int packet_num_{0};
+  PlaybackEvent::PlaybackEventType current_state_{PlaybackEvent::UNKNOWN};
+  std::chrono::steady_clock::time_point next_check_time_{};
+  android::bluetooth::test_vendor_lib::model::devices::ScriptedBeaconBleAdProto::BleAdvertisementList ble_ad_list_;
+};
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto
new file mode 100644
index 0000000..588519c
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto
@@ -0,0 +1,33 @@
+syntax = "proto2";
+
+package android.bluetooth.test_vendor_lib.model.devices.ScriptedBeaconBleAdProto;
+
+option optimize_for = LITE_RUNTIME;
+
+message BleAdvertisement {
+  optional bytes payload = 1;
+  optional bytes mac_address = 2;
+  optional uint32 delay_before_send_ms = 3;
+}
+
+message BleAdvertisementList {
+  repeated BleAdvertisement advertisements = 1;
+}
+
+message PlaybackEvent {
+  // These events should occur in order, starting from INITIALIZED
+  enum PlaybackEventType {
+    UNKNOWN = 0;
+    INITIALIZED = 1;
+    SCANNED_ONCE = 2;
+    WAITING_FOR_FILE = 3;
+    WAITING_FOR_FILE_TO_BE_READABLE = 4;
+    PARSING_FILE = 5;
+    PLAYBACK_STARTED = 6;
+    PLAYBACK_ENDED = 7;
+    // Error conditions
+    FILE_PARSING_FAILED = 8;
+  }
+  optional PlaybackEventType type = 1;
+  optional uint64 secs_since_epoch = 2;
+}
diff --git a/vendor_libs/test_vendor_lib/model/devices/sniffer.h b/vendor_libs/test_vendor_lib/model/devices/sniffer.h
index a5c7277..590b182 100644
--- a/vendor_libs/test_vendor_lib/model/devices/sniffer.h
+++ b/vendor_libs/test_vendor_lib/model/devices/sniffer.h
@@ -51,7 +51,7 @@
 
  private:
   static bool registered_;
-  Address device_to_sniff_;
+  Address device_to_sniff_{};
 };
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/setup/async_manager.cc b/vendor_libs/test_vendor_lib/model/setup/async_manager.cc
index 092271d..128cc5e 100644
--- a/vendor_libs/test_vendor_lib/model/setup/async_manager.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/async_manager.cc
@@ -262,8 +262,8 @@
   std::map<int, ReadCallback> watched_shared_fds_;
 
   // A pair of FD to send information to the reading thread
-  int notification_listen_fd_;
-  int notification_write_fd_;
+  int notification_listen_fd_{};
+  int notification_write_fd_{};
 };
 
 // Async task manager implementation
@@ -335,7 +335,7 @@
     // public or gets more complex
     std::chrono::steady_clock::time_point time;
     bool periodic;
-    std::chrono::milliseconds period;
+    std::chrono::milliseconds period{};
     TaskCallback callback;
     AsyncTaskId task_id;
   };
diff --git a/vendor_libs/test_vendor_lib/model/setup/phy_layer.h b/vendor_libs/test_vendor_lib/model/setup/phy_layer.h
index 7f0fa33..00a3067 100644
--- a/vendor_libs/test_vendor_lib/model/setup/phy_layer.h
+++ b/vendor_libs/test_vendor_lib/model/setup/phy_layer.h
@@ -33,7 +33,7 @@
         transmit_to_device_(device_receive) {}
 
   virtual void Send(
-      const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet) = 0;
+      std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet) = 0;
   virtual void Send(model::packets::LinkLayerPacketView packet) = 0;
 
   virtual void Receive(model::packets::LinkLayerPacketView packet) = 0;
diff --git a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc
index b963ce0..1930725 100644
--- a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc
@@ -35,8 +35,7 @@
         device_receive,
     uint32_t device_id) {
   std::shared_ptr<PhyLayer> new_phy = std::make_shared<PhyLayerImpl>(
-      phy_type_, next_id_++, device_receive, device_id,
-      std::shared_ptr<PhyLayerFactory>(this));
+      phy_type_, next_id_++, device_receive, device_id, this);
   phy_layers_.push_back(new_phy);
   return new_phy;
 }
@@ -51,6 +50,12 @@
   }
 }
 
+void PhyLayerFactory::UnregisterAllPhyLayers() {
+  for (const auto& phy : phy_layers_) {
+    UnregisterPhyLayer(phy->GetId());
+  }
+}
+
 void PhyLayerFactory::Send(
     const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
     uint32_t id) {
@@ -107,7 +112,7 @@
     Phy::Type phy_type, uint32_t id,
     const std::function<void(model::packets::LinkLayerPacketView)>&
         device_receive,
-    uint32_t device_id, const std::shared_ptr<PhyLayerFactory> factory)
+    uint32_t device_id, PhyLayerFactory* factory)
     : PhyLayer(phy_type, id, device_receive, device_id), factory_(factory) {}
 
 PhyLayerImpl::~PhyLayerImpl() {
diff --git a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h
index 7d46733..273bbc8 100644
--- a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h
+++ b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h
@@ -44,13 +44,15 @@
 
   void UnregisterPhyLayer(uint32_t id);
 
+  void UnregisterAllPhyLayers();
+
   virtual void TimerTick();
 
   virtual std::string ToString() const;
 
  protected:
   virtual void Send(
-      const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
+      std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
       uint32_t id);
   virtual void Send(model::packets::LinkLayerPacketView packet, uint32_t id);
 
@@ -66,22 +68,19 @@
   PhyLayerImpl(Phy::Type phy_type, uint32_t id,
                const std::function<void(model::packets::LinkLayerPacketView)>&
                    device_receive,
-               uint32_t device_id,
-               const std::shared_ptr<PhyLayerFactory> factory);
-  virtual ~PhyLayerImpl() override;
+               uint32_t device_id, PhyLayerFactory* factory);
+  ~PhyLayerImpl() override;
 
-  virtual void Send(
-      const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet)
-      override;
+  void Send(
+      std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet) override;
   void Send(model::packets::LinkLayerPacketView packet) override;
   void Receive(model::packets::LinkLayerPacketView packet) override;
   void Unregister() override;
   bool IsFactoryId(uint32_t factory_id) override;
   void TimerTick() override;
 
-  uint32_t device_id_;
 
  private:
-  std::shared_ptr<PhyLayerFactory> factory_;
+  PhyLayerFactory* factory_;
 };
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_channel_transport.cc b/vendor_libs/test_vendor_lib/model/setup/test_channel_transport.cc
index d42bc72..d9dfdb9 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_channel_transport.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/test_channel_transport.cc
@@ -30,7 +30,7 @@
 namespace test_vendor_lib {
 
 int TestChannelTransport::SetUp(int port) {
-  struct sockaddr_in listen_address;
+  struct sockaddr_in listen_address {};
   socklen_t sockaddr_in_size = sizeof(struct sockaddr_in);
   memset(&listen_address, 0, sockaddr_in_size);
 
@@ -74,17 +74,17 @@
   listen_fd_ = -1;
 }
 
-int TestChannelTransport::Accept(int listen_fd_) {
-  int accept_fd = -1;
+int TestChannelTransport::Accept(int listen_fd) {
+  int accept_fd;
 
-  OSI_NO_INTR(accept_fd = accept(listen_fd_, NULL, NULL));
+  OSI_NO_INTR(accept_fd = accept(listen_fd, NULL, NULL));
 
   if (accept_fd < 0) {
     LOG_INFO("Error accepting test channel connection errno=%d (%s).", errno, strerror(errno));
 
     if (errno != EAGAIN && errno != EWOULDBLOCK) {
-      LOG_ERROR("Closing listen_fd_ (won't try again).");
-      close(listen_fd_);
+      LOG_ERROR("Closing listen_fd (won't try again).");
+      close(listen_fd);
       return -1;
     }
   }
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_command_handler.cc b/vendor_libs/test_vendor_lib/model/setup/test_command_handler.cc
index a2b8b2e..edc3910 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_command_handler.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/test_command_handler.cc
@@ -220,7 +220,7 @@
     return;
   }
   size_t device_id = std::stoi(args[0]);
-  Address device_address;
+  Address device_address{};
   Address::FromString(args[1], device_address);
   model_.SetDeviceAddress(device_id, device_address);
   response_string_ = "set_device_address " + args[0];
@@ -234,7 +234,15 @@
     LOG_INFO("SetTimerPeriod takes 1 argument");
   }
   size_t period = std::stoi(args[0]);
-  model_.SetTimerPeriod(std::chrono::milliseconds(period));
+  if (period != 0) {
+    response_string_ = "set timer period to ";
+    response_string_ += args[0];
+    model_.SetTimerPeriod(std::chrono::milliseconds(period));
+  } else {
+    response_string_ = "invalid timer period ";
+    response_string_ += args[0];
+  }
+  send_response_(response_string_);
 }
 
 void TestCommandHandler::StartTimer(const vector<std::string>& args) {
@@ -242,6 +250,8 @@
     LOG_INFO("Unused args: arg[0] = %s", args[0].c_str());
   }
   model_.StartTimer();
+  response_string_ = "timer started";
+  send_response_(response_string_);
 }
 
 void TestCommandHandler::StopTimer(const vector<std::string>& args) {
@@ -249,6 +259,8 @@
     LOG_INFO("Unused args: arg[0] = %s", args[0].c_str());
   }
   model_.StopTimer();
+  response_string_ = "timer stopped";
+  send_response_(response_string_);
 }
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_model.cc b/vendor_libs/test_vendor_lib/model/setup/test_model.cc
index 688b7dc..d0c1cbb 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_model.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/test_model.cc
@@ -23,6 +23,7 @@
 #include "model/devices/classic.h"
 #include "model/devices/keyboard.h"
 #include "model/devices/remote_loopback_device.h"
+#include "model/devices/scripted_beacon.h"
 #include "model/devices/sniffer.h"
 
 #include <memory>
@@ -63,6 +64,7 @@
   example_devices_.push_back(std::make_shared<CarKit>());
   example_devices_.push_back(std::make_shared<Classic>());
   example_devices_.push_back(std::make_shared<Sniffer>());
+  example_devices_.push_back(std::make_shared<ScriptedBeacon>());
   example_devices_.push_back(std::make_shared<RemoteLoopbackDevice>());
 }
 
@@ -89,75 +91,69 @@
 }
 
 size_t TestModel::Add(std::shared_ptr<Device> new_dev) {
-  devices_counter_++;
-  devices_[devices_counter_] = new_dev;
-  return devices_counter_;
+  devices_.push_back(new_dev);
+  return devices_.size() - 1;
 }
 
 void TestModel::Del(size_t dev_index) {
-  auto device = devices_.find(dev_index);
-  if (device == devices_.end()) {
-    LOG_WARN("Del: can't find device!");
+  if (dev_index >= devices_.size() || devices_[dev_index] == nullptr) {
+    LOG_WARN("Unknown device %zu", dev_index);
     return;
   }
-  devices_.erase(dev_index);
+  devices_[dev_index]->UnregisterPhyLayers();
+  devices_[dev_index] = nullptr;
 }
 
 size_t TestModel::AddPhy(Phy::Type phy_type) {
-  phys_counter_++;
-  std::shared_ptr<PhyLayerFactory> new_phy = std::make_shared<PhyLayerFactory>(phy_type, phys_counter_);
-  phys_[phys_counter_] = new_phy;
-  return phys_counter_;
+  size_t factory_id = phys_.size();
+  phys_.emplace_back(phy_type, factory_id);
+  return factory_id;
 }
 
 void TestModel::DelPhy(size_t phy_index) {
-  auto phy = phys_.find(phy_index);
-  if (phy == phys_.end()) {
-    LOG_WARN("DelPhy: can't find device!");
+  if (phy_index >= phys_.size()) {
+    LOG_WARN("Unknown phy at index %zu", phy_index);
     return;
   }
-  phys_.erase(phy_index);
+  phys_[phy_index].UnregisterAllPhyLayers();
 }
 
 void TestModel::AddDeviceToPhy(size_t dev_index, size_t phy_index) {
-  auto device = devices_.find(dev_index);
-  if (device == devices_.end()) {
-    LOG_WARN("%s: can't find device!", __func__);
+  if (dev_index >= devices_.size() || devices_[dev_index] == nullptr) {
+    LOG_WARN("Unknown device %zu", dev_index);
     return;
   }
-  auto phy = phys_.find(phy_index);
-  if (phy == phys_.end()) {
-    LOG_WARN("%s: can't find phy!", __func__);
+  if (phy_index >= phys_.size()) {
+    LOG_WARN("Can't find phy %zu", phy_index);
     return;
   }
-  auto dev = device->second;
-  dev->RegisterPhyLayer(phy->second->GetPhyLayer(
+  auto dev = devices_[dev_index];
+  dev->RegisterPhyLayer(phys_[phy_index].GetPhyLayer(
       [dev](model::packets::LinkLayerPacketView packet) {
         dev->IncomingPacket(packet);
       },
-      device->first));
+      dev_index));
 }
 
 void TestModel::DelDeviceFromPhy(size_t dev_index, size_t phy_index) {
-  auto device = devices_.find(dev_index);
-  if (device == devices_.end()) {
-    LOG_WARN("%s: can't find device!", __func__);
+  if (dev_index >= devices_.size() || devices_[dev_index] == nullptr) {
+    LOG_WARN("Unknown device %zu", dev_index);
     return;
   }
-  auto phy = phys_.find(phy_index);
-  if (phy == phys_.end()) {
-    LOG_WARN("%s: can't find phy!", __func__);
+  if (phy_index >= phys_.size()) {
+    LOG_WARN("Can't find phy %zu", phy_index);
     return;
   }
-  device->second->UnregisterPhyLayer(phy->second->GetType(), phy->second->GetFactoryId());
+  devices_[dev_index]->UnregisterPhyLayer(phys_[phy_index].GetType(),
+                                          phys_[phy_index].GetFactoryId());
 }
 
 void TestModel::AddLinkLayerConnection(int socket_fd, Phy::Type phy_type) {
   std::shared_ptr<Device> dev = LinkLayerSocketDevice::Create(socket_fd, phy_type);
   int index = Add(dev);
-  for (auto& phy : phys_) {
-    if (phy_type == phy.second->GetType()) {
-      AddDeviceToPhy(index, phy.first);
+  for (size_t i = 0; i < phys_.size(); i++) {
+    if (phy_type == phys_[i].GetType()) {
+      AddDeviceToPhy(index, i);
     }
   }
 }
@@ -185,8 +181,8 @@
 
   dev->Initialize({"IgnoredTypeName", addr});
   LOG_INFO("initialized %s", addr.c_str());
-  for (auto& phy : phys_) {
-    AddDeviceToPhy(index, phy.first);
+  for (size_t i = 0; i < phys_.size(); i++) {
+    AddDeviceToPhy(index, i);
   }
   dev->RegisterTaskScheduler(schedule_task_);
   dev->RegisterTaskCancel(cancel_task_);
@@ -194,51 +190,58 @@
 }
 
 void TestModel::OnHciConnectionClosed(int socket_fd, size_t index) {
-  auto device = devices_.find(index);
-  if (device == devices_.end()) {
-    LOG_WARN("OnHciConnectionClosed: can't find device!");
+  if (index >= devices_.size() || devices_[index] == nullptr) {
+    LOG_WARN("Unknown device %zu", index);
     return;
   }
   int close_result = close(socket_fd);
   ASSERT_LOG(close_result == 0, "can't close: %s", strerror(errno));
-  device->second->UnregisterPhyLayers();
-  devices_.erase(index);
+  devices_[index]->UnregisterPhyLayers();
+  devices_[index] = nullptr;
 }
 
 void TestModel::SetDeviceAddress(size_t index, Address address) {
-  auto device = devices_.find(index);
-  if (device == devices_.end()) {
-    LOG_WARN("SetDeviceAddress can't find device!");
+  if (index >= devices_.size() || devices_[index] == nullptr) {
+    LOG_WARN("Can't find device %zu", index);
     return;
   }
-  device->second->SetAddress(address);
+  devices_[index]->SetAddress(address);
 }
 
 const std::string& TestModel::List() {
   list_string_ = "";
   list_string_ += " Devices: \r\n";
-  for (auto& dev : devices_) {
-    list_string_ += "  " + std::to_string(dev.first) + ":";
-    list_string_ += dev.second->ToString() + " \r\n";
+  for (size_t i = 0; i < devices_.size(); i++) {
+    list_string_ += "  " + std::to_string(i) + ":";
+    if (devices_[i] == nullptr) {
+      list_string_ += " deleted \r\n";
+    } else {
+      list_string_ += devices_[i]->ToString() + " \r\n";
+    }
   }
   list_string_ += " Phys: \r\n";
-  for (auto& phy : phys_) {
-    list_string_ += "  " + std::to_string(phy.first) + ":";
-    list_string_ += phy.second->ToString() + " \r\n";
+  for (size_t i = 0; i < phys_.size(); i++) {
+    list_string_ += "  " + std::to_string(i) + ":";
+    list_string_ += phys_[i].ToString() + " \r\n";
   }
   return list_string_;
 }
 
 void TestModel::TimerTick() {
-  for (auto dev = devices_.begin(); dev != devices_.end();) {
-    auto tmp = dev;
-    dev++;
-    tmp->second->TimerTick();
+  for (const auto& dev : devices_) {
+    if (dev != nullptr) {
+      dev->TimerTick();
+    }
   }
 }
 
 void TestModel::Reset() {
   StopTimer();
+  for (const auto& dev : devices_) {
+    if (dev != nullptr) {
+      dev->UnregisterPhyLayers();
+    }
+  }
   devices_.clear();
   phys_.clear();
 }
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_model.h b/vendor_libs/test_vendor_lib/model/setup/test_model.h
index d651f50..e8f6cdd 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_model.h
+++ b/vendor_libs/test_vendor_lib/model/setup/test_model.h
@@ -38,6 +38,9 @@
             std::function<void(AsyncTaskId)> cancel, std::function<int(const std::string&, int)> connect_to_remote);
   ~TestModel() = default;
 
+  TestModel(TestModel& model) = delete;
+  TestModel& operator=(const TestModel& model) = delete;
+
   // Commands:
 
   // Add a device, return its index
@@ -85,10 +88,8 @@
   void Reset();
 
  private:
-  std::map<size_t, std::shared_ptr<PhyLayerFactory>> phys_;
-  size_t phys_counter_ = 0;
-  std::map<size_t, std::shared_ptr<Device>> devices_;
-  size_t devices_counter_ = 0;
+  std::vector<PhyLayerFactory> phys_;
+  std::vector<std::shared_ptr<Device>> devices_;
   std::string list_string_;
 
   // Callbacks to schedule tasks.
@@ -99,10 +100,7 @@
   std::function<int(const std::string&, int)> connect_to_remote_;
 
   AsyncTaskId timer_tick_task_{kInvalidTaskId};
-  std::chrono::milliseconds timer_period_;
-
-  TestModel(TestModel& model) = delete;
-  TestModel& operator=(const TestModel& model) = delete;
+  std::chrono::milliseconds timer_period_{};
 
   std::vector<std::shared_ptr<Device>> example_devices_;
 };
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
index 2d601cd..fc5fa3e 100644
--- a/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
+++ b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
@@ -36,8 +36,8 @@
     REMOTE_NAME_REQUEST = 0x1D,
     REMOTE_NAME_REQUEST_RESPONSE = 0x1E,
     SCO = 0x1F,
-    COMMAND = 0x23, // Remove
-    RESPONSE = 0x24, // Remove
+    LE_ENCRYPT_CONNECTION = 0x20,
+    LE_ENCRYPT_CONNECTION_RESPONSE = 0x21,
 }
 
 packet LinkLayerPacket {
@@ -47,15 +47,6 @@
   _body_,
 }
 
-packet Command : LinkLayerPacket (type = COMMAND) {
-  _payload_,
-}
-
-packet Response : LinkLayerPacket (type = RESPONSE) {
-  opcode : 16,
-  _payload_,
-}
-
 packet AclPacket : LinkLayerPacket (type = ACL) {
   _payload_,
 }
@@ -234,3 +225,15 @@
   _payload_,
 }
 
+packet LeEncryptConnection : LinkLayerPacket (type = LE_ENCRYPT_CONNECTION) {
+  rand : 8[8],
+  ediv : 16,
+  ltk : 8[16],
+}
+
+packet LeEncryptConnectionResponse : LinkLayerPacket (type = LE_ENCRYPT_CONNECTION_RESPONSE) {
+  rand : 8[8],
+  ediv : 16,
+  ltk : 8[16],
+}
+
diff --git a/vendor_libs/test_vendor_lib/scripts/hci_socket.py b/vendor_libs/test_vendor_lib/scripts/hci_socket.py
index 0c425af..427b8d4 100644
--- a/vendor_libs/test_vendor_lib/scripts/hci_socket.py
+++ b/vendor_libs/test_vendor_lib/scripts/hci_socket.py
@@ -69,35 +69,36 @@
 import struct
 import sys
 from scapy.all import *
+import time
 """ Add some more SCAPY stuff"""
 
 
 class HCI_Cmd_Create_Connection(Packet):
-  name = 'Create Connection'
-  fields_desc = [
-      LEMACField('addr', None),
-      LEShortField('packet_type', 8),
-      ByteEnumField('page_scan_repetition_mode', 0, {
-          0: 'R0',
-          1: 'R1',
-          2: 'R2'
-      }),
-      ByteEnumField('rsvd', 0, {0: 'Reserved'}),
-      LEShortField('clock_offset', 0),
-      ByteEnumField('allow_role_switch', 1, {
-          0: 'false',
-          1: 'true'
-      }),
-  ]
+    name = 'Create Connection'
+    fields_desc = [
+        LEMACField('addr', None),
+        LEShortField('packet_type', 8),
+        ByteEnumField('page_scan_repetition_mode', 0, {
+            0: 'R0',
+            1: 'R1',
+            2: 'R2'
+        }),
+        ByteEnumField('rsvd', 0, {0: 'Reserved'}),
+        LEShortField('clock_offset', 0),
+        ByteEnumField('allow_role_switch', 1, {
+            0: 'false',
+            1: 'true'
+        }),
+    ]
 
 
 class HCI_Cmd_Inquiry(Packet):
-  name = 'Inquiry'
-  fields_desc = [
-      X3BytesField('LAP', 0x9e8b0),
-      ByteField('length', 1),
-      ByteField('max_responses', 0),
-  ]
+    name = 'Inquiry'
+    fields_desc = [
+        X3BytesField('LAP', 0x9e8b0),
+        ByteField('length', 1),
+        ByteField('max_responses', 0),
+    ]
 
 
 bind_layers(HCI_Command_Hdr, HCI_Cmd_Inquiry, opcode=0x0401)
@@ -105,337 +106,327 @@
 
 
 class HCI_Event_Inquiry_Result(Packet):
-  name = 'Inquiry Result'
-  fields_desc = [
-      ByteField('num_responses', 0),
-      LEMACField('addr', None),
-      ByteEnumField('page_scan_repetition_mode', 0, {
-          0: 'R0',
-          1: 'R1',
-          2: 'R2'
-      }),
-      LEShortEnumField('rsvd', 0, {0: 'Reserved'}),
-      X3BytesField('class_of_device', 0),
-      LEShortField('clock_offset', 0),
-  ]
+    name = 'Inquiry Result'
+    fields_desc = [
+        ByteField('num_responses', 0),
+        LEMACField('addr', None),
+        ByteEnumField('page_scan_repetition_mode', 0, {
+            0: 'R0',
+            1: 'R1',
+            2: 'R2'
+        }),
+        LEShortEnumField('rsvd', 0, {0: 'Reserved'}),
+        X3BytesField('class_of_device', 0),
+        LEShortField('clock_offset', 0),
+    ]
 
 
 class HCI_Event_Connection_Complete(Packet):
-  name = 'Connection Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      LEMACField('addr', None),
-      ByteField('link_type', 1),
-      ByteField('encryption_mode', 0),
-  ]
+    name = 'Connection Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        LEMACField('addr', None),
+        ByteField('link_type', 1),
+        ByteField('encryption_mode', 0),
+    ]
 
 
 class HCI_Event_Remote_Name_Request_Complete(Packet):
-  name = 'Remote Name Request Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEMACField('addr', None),
-  ]
+    name = 'Remote Name Request Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEMACField('addr', None),
+    ]
 
 
 class HCI_Event_Read_Remote_Supported_Features_Complete(Packet):
-  name = 'Read Remote Supported Features Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      XLELongField('features', 0x0123456789abcdef),
-  ]
+    name = 'Read Remote Supported Features Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        XLELongField('features', 0x0123456789abcdef),
+    ]
 
 
 class HCI_Event_Read_Remote_Version_Information_Complete(Packet):
-  name = 'Read Remote Version Information Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      ByteField('version', 0),
-      LEShortField('manufacturer_name', 0),
-      LEShortField('subversion', 0),
-  ]
+    name = 'Read Remote Version Information Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        ByteField('version', 0),
+        LEShortField('manufacturer_name', 0),
+        LEShortField('subversion', 0),
+    ]
 
 
 class HCI_Event_Read_Clock_Offset_Complete(Packet):
-  name = 'Read Clock Offset Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      LEShortField('offset', 0xffff),
-  ]
+    name = 'Read Clock Offset Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        LEShortField('offset', 0xffff),
+    ]
 
 
 class HCI_Event_Read_Remote_Extended_Features_Complete(Packet):
-  name = 'Read Remote Supported Features Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      ByteField('page_number', 0),
-      ByteField('max_page_number', 0),
-      XLELongField('features', 0x0123456789abcdef),
-  ]
+    name = 'Read Remote Supported Features Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        ByteField('page_number', 0),
+        ByteField('max_page_number', 0),
+        XLELongField('features', 0x0123456789abcdef),
+    ]
 
 
 class HCI_Event_Extended_Inquiry_Result(Packet):
-  name = 'Extended Inquiry Result'
-  fields_desc = [
-      ByteField('num_responses', 1),
-      LEMACField('addr', None),
-      ByteEnumField('page_scan_repetition_mode', 0, {
-          0: 'R0',
-          1: 'R1',
-          2: 'R2'
-      }),
-      ByteEnumField('rsvd', 0, {0: 'Reserved'}),
-      X3BytesField('class_of_device', 0),
-      LEShortField('clock_offset', 0),
-      SignedByteField('rssi', -20),
-      PacketListField('extended_inquiry_response', [], EIR_Hdr, 1),
-  ]
+    name = 'Extended Inquiry Result'
+    fields_desc = [
+        ByteField('num_responses', 1),
+        LEMACField('addr', None),
+        ByteEnumField('page_scan_repetition_mode', 0, {
+            0: 'R0',
+            1: 'R1',
+            2: 'R2'
+        }),
+        ByteEnumField('rsvd', 0, {0: 'Reserved'}),
+        X3BytesField('class_of_device', 0),
+        LEShortField('clock_offset', 0),
+        SignedByteField('rssi', -20),
+        PacketListField('extended_inquiry_response', [], EIR_Hdr, 1),
+    ]
 
 
 bind_layers(HCI_Event_Hdr, HCI_Event_Inquiry_Result, code=0x02)
 bind_layers(HCI_Event_Hdr, HCI_Event_Connection_Complete, code=0x03)
 bind_layers(HCI_Event_Hdr, HCI_Event_Remote_Name_Request_Complete, code=0x07)
-bind_layers(
-    HCI_Event_Hdr, HCI_Event_Read_Remote_Supported_Features_Complete, code=0x0b)
-bind_layers(
-    HCI_Event_Hdr,
-    HCI_Event_Read_Remote_Version_Information_Complete,
-    code=0x0c)
+bind_layers(HCI_Event_Hdr, HCI_Event_Read_Remote_Supported_Features_Complete, code=0x0b)
+bind_layers(HCI_Event_Hdr, HCI_Event_Read_Remote_Version_Information_Complete, code=0x0c)
 bind_layers(HCI_Event_Hdr, HCI_Event_Read_Clock_Offset_Complete, code=0x1c)
-bind_layers(
-    HCI_Event_Hdr, HCI_Event_Read_Remote_Extended_Features_Complete, code=0x23)
+bind_layers(HCI_Event_Hdr, HCI_Event_Read_Remote_Extended_Features_Complete, code=0x23)
 bind_layers(HCI_Event_Hdr, HCI_Event_Extended_Inquiry_Result, code=0x2f)
 """ END SCAPY stuff"""
 
 
 class HCISocket(SuperSocket):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self.done_ = False
-    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    s.connect(('localhost', port))
-    self.ins = self.outs = s
-    self.packets_ = queue.Queue()
-    self.rx_thread_ = threading.Thread(target=self.rx_thread_body)
-    self.rx_thread_.start()
+    def __init__(self, port):
+        self.done_ = False
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.connect(('localhost', port))
+        self.ins = self.outs = s
+        self.packets_ = queue.Queue()
+        self.rx_thread_ = threading.Thread(target=self.rx_thread_body)
+        self.rx_thread_.start()
 
-  def rx_bytes(self, size):
-    while not self.done_:
-      raw_bytes = b''
-      while len(raw_bytes) < size and not self.done_:
-        more_raw_bytes = self.ins.recv(min(size - len(raw_bytes), 2048))
-        if more_raw_bytes:
-          raw_bytes += more_raw_bytes
-      return raw_bytes
+    def rx_bytes(self, size):
+        while not self.done_:
+            raw_bytes = b''
+            while len(raw_bytes) < size and not self.done_:
+                more_raw_bytes = self.ins.recv(min(size - len(raw_bytes), 2048))
+                if more_raw_bytes:
+                    raw_bytes += more_raw_bytes
+            return raw_bytes
 
-  def rx_thread_body(self):
-    while not self.done_:
-      payload_length = 0
-      # Read the type
-      type_byte = self.rx_bytes(1)
-      if not type_byte:
-        continue
-      # Read the Header
-      header = b''
-      if type_byte == b'\x01':  # Command
-        header = self.rx_bytes(3)
-        if not header:
-          continue
-        payload_length = header[2]
-      elif type_byte == b'\x02':  # ACL
-        header = self.rx_bytes(4)
-        if not header:
-          continue
-        payload_length = header[3] << 8
-        payload_length |= header[2]
-      elif type_byte == b'\x03':  # SCO
-        header = self.rx_bytes(3)
-        if not header:
-          continue
-        payload_length = header[2]
-      elif type_byte == b'\x04':  # Event
-        header = self.rx_bytes(2)
-        if not header:
-          continue
-        payload_length = header[1]
-      else:
+    def rx_thread_body(self):
+        while not self.done_:
+            payload_length = 0
+            # Read the type
+            type_byte = self.rx_bytes(1)
+            if not type_byte:
+                continue
+            # Read the Header
+            header = b''
+            if type_byte == b'\x01':  # Command
+                header = self.rx_bytes(3)
+                if not header:
+                    continue
+                payload_length = header[2]
+            elif type_byte == b'\x02':  # ACL
+                header = self.rx_bytes(4)
+                if not header:
+                    continue
+                payload_length = header[3] << 8
+                payload_length |= header[2]
+            elif type_byte == b'\x03':  # SCO
+                header = self.rx_bytes(3)
+                if not header:
+                    continue
+                payload_length = header[2]
+            elif type_byte == b'\x04':  # Event
+                header = self.rx_bytes(2)
+                if not header:
+                    continue
+                payload_length = header[1]
+            else:
+                self.done_ = True
+                print('Rx: type_byte ' + hex(type_byte[0]))
+            # Read the Payload
+            payload = self.rx_bytes(payload_length) if payload_length != 0 else b''
+            packet_bytes = type_byte + header + payload
+            packet = HCI_Hdr(packet_bytes)
+            print('Rx: ' + packet.__repr__())
+            self.packets_.put(packet)
+
+    def get_packet(self):
+        if self.packets_.empty():
+            return False
+        return self.packets_.get()
+
+    def tell_rx_thread_to_quit(self):
         self.done_ = True
-        print('Rx: type_byte ' + hex(type_byte[0]))
-      # Read the Payload
-      payload = self.rx_bytes(payload_length) if payload_length != 0 else b''
-      packet_bytes = type_byte + header + payload
-      packet = HCI_Hdr(packet_bytes)
-      print('Rx: ' + packet.__repr__())
-      self.packets_.put(packet)
-
-  def get_packet(self):
-    if self.packets_.empty():
-      return False
-    return self.packets_.get()
-
-  def tell_rx_thread_to_quit(self):
-    self.done_ = True
-    self.rx_thread_.join()
+        self.rx_thread_.join()
 
 
 class HCIShell(cmd.Cmd):
-  """Shell for sending binary data to a port.
+    """Shell for sending binary data to a port.
 
   """
 
-  def __init__(self, hci):
-    cmd.Cmd.__init__(self)
-    self._hci = hci
+    def __init__(self, hci):
+        cmd.Cmd.__init__(self)
+        self._hci = hci
 
-  def do_send(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_send(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._hci.send_binary(args.split())
+        self._hci.send_binary(args.split())
 
-  def do_connect(self, args):
-    """Arguments: bluetooth_address xx:xx:xx:xx:xx:xx, timeout (seconds)
+    def do_connect(self, args):
+        """Arguments: bluetooth_address xx:xx:xx:xx:xx:xx, timeout (seconds)
 
     """
-    split_args = args.split()
-    address = split_args[0] if len(split_args) > 0 else 'NULL'
-    timeout = int(split_args[1]) if len(split_args) > 1 else 2
-    num_responses = 0
-    connect = HCI_Hdr(type='Command') / HCI_Command_Hdr(
-        opcode=0x0405) / HCI_Cmd_Create_Connection(addr=address)
-    self._hci.send(connect)
-    status = None
-    while status == None:
-      response = self._hci.get_packet()
-      if response == False:
-        continue
-      if response[HCI_Hdr].type == HCI_Hdr(
-          type='Event'
-      ).type and response[HCI_Event_Hdr].code == 0xf and response[
-          HCI_Event_Command_Status].opcode == connect[HCI_Command_Hdr].opcode:
-        status = response[HCI_Event_Command_Status].status
-    if status != HCI_Event_Command_Status(status='pending').status:
-      print('Connection failed with status = ' + str(status))
-      return
+        split_args = args.split()
+        address = split_args[0] if len(split_args) > 0 else 'NULL'
+        timeout = int(split_args[1]) if len(split_args) > 1 else 2
+        num_responses = 0
+        connect = HCI_Hdr(type='Command') / HCI_Command_Hdr(opcode=0x0405) / HCI_Cmd_Create_Connection(addr=address)
+        self._hci.send(connect)
+        status = None
+        while status == None:
+            response = self._hci.get_packet()
+            if response == False:
+                continue
+            if response[HCI_Hdr].type == HCI_Hdr(
+                    type='Event'
+            ).type and response[HCI_Event_Hdr].code == 0xf and response[HCI_Event_Command_Status].opcode == connect[HCI_Command_Hdr].opcode:
+                status = response[HCI_Event_Command_Status].status
+        if status != HCI_Event_Command_Status(status='pending').status:
+            print('Connection failed with status = ' + str(status))
+            return
 
-    handle = None
-    while handle == None:
-      connection_complete = self._hci.get_packet()
-      if connection_complete == False:
-        continue
-      if (connection_complete[HCI_Hdr].type == HCI_Hdr(type='Event').type) and (
-          connection_complete[HCI_Event_Hdr].code == 0x3):
-        status = connection_complete[HCI_Event_Connection_Complete].status
-        if status != 0:
-          print('Connection complete with failed status = ' + str(status))
-          return
-        handle = connection_complete[HCI_Event_Connection_Complete].handle
-        print('Connection established with handle ' + str(handle))
-        connection_complete.show()
-        hexdump(connection_complete)
+        handle = None
+        while handle == None:
+            connection_complete = self._hci.get_packet()
+            if connection_complete == False:
+                continue
+            if (connection_complete[HCI_Hdr].type == HCI_Hdr(type='Event').type) and (
+                    connection_complete[HCI_Event_Hdr].code == 0x3):
+                status = connection_complete[HCI_Event_Connection_Complete].status
+                if status != 0:
+                    print('Connection complete with failed status = ' + str(status))
+                    return
+                handle = connection_complete[HCI_Event_Connection_Complete].handle
+                print('Connection established with handle ' + str(handle))
+                connection_complete.show()
+                hexdump(connection_complete)
 
-    l2cap_done = False
-    while l2cap_done == None:
-      l2cap_req = self._hci.get_packet()
-      if l2cap_req == False:
-        continue
-      if (l2cap_req[HCI_Hdr].type == HCI_Hdr(type='ACL Data').type) and (
-          l2cap_req[L2CAP_Hdr].cid == L2CAP_Hdr(cid='control').cid) and (
-              l2cap_req[L2CAP_CmdHdr].code == L2CAP_CmdHdr(code='info_req').code
-          ) and (l2cap_req[L2CAP_InfoReq].type == L2CAP_InfoReq(
-              type='FEAT_MASK').type):
-        print('Send Features packet' + HCI_Hdr(type='ACL Data') / HCI_ACL_Hdr(
-            handle=l2cap_req[HCI_ACL_Hdr].handle, PB=0, BC=2, len=16) /
-              L2CAP_Hdr(len=12, cid='control') /
-              L2CAP_CmdHdr(code='info_resp', id=146, len=8) / L2CAP_InfoResp(
-                  type=l2cap_req[L2CAP_InfoResp].type,
-                  result='success',
-                  data=b'\xb8\x00\x00\x00').__repr__())
-        self._hci.send(
-            HCI_Hdr(type='ACL Data') / HCI_ACL_Hdr(
-                handle=l2cap_req[HCI_ACL_Hdr].handle, PB=0, BC=2, len=16) /
-            L2CAP_Hdr(len=12, cid='control') / L2CAP_CmdHdr(
-                code='info_resp', id=146, len=8) / L2CAP_InfoResp(
-                    type=l2cap_req[L2CAP_InfoResp].type,
-                    result='success',
-                    data=b'\xb8\x00\x00\x00'))
+        l2cap_done = False
+        while l2cap_done == None:
+            l2cap_req = self._hci.get_packet()
+            if l2cap_req == False:
+                continue
+            if (l2cap_req[HCI_Hdr].type == HCI_Hdr(type='ACL Data').type) and (l2cap_req[L2CAP_Hdr].cid == L2CAP_Hdr(
+                    cid='control').cid) and (l2cap_req[L2CAP_CmdHdr].code == L2CAP_CmdHdr(code='info_req').code) and (
+                        l2cap_req[L2CAP_InfoReq].type == L2CAP_InfoReq(type='FEAT_MASK').type):
+                print('Send Features packet' +
+                      HCI_Hdr(type='ACL Data') / HCI_ACL_Hdr(handle=l2cap_req[HCI_ACL_Hdr].handle, PB=0, BC=2, len=16) /
+                      L2CAP_Hdr(len=12, cid='control') / L2CAP_CmdHdr(code='info_resp', id=146, len=8) / L2CAP_InfoResp(
+                          type=l2cap_req[L2CAP_InfoResp].type, result='success', data=b'\xb8\x00\x00\x00').__repr__())
+                self._hci.send(
+                    HCI_Hdr(type='ACL Data') / HCI_ACL_Hdr(handle=l2cap_req[HCI_ACL_Hdr].handle, PB=0, BC=2, len=16) /
+                    L2CAP_Hdr(len=12, cid='control') / L2CAP_CmdHdr(code='info_resp', id=146, len=8) / L2CAP_InfoResp(
+                        type=l2cap_req[L2CAP_InfoResp].type, result='success', data=b'\xb8\x00\x00\x00'))
 
-  def do_le_scan(self, args):
-    """Arguments: enable (0 or 1), filter duplicates (0 or 1) Print the scan responses from reachable devices
+    def do_le_scan(self, args):
+        """Arguments: enable (0 or 1), filter duplicates (0 or 1) Print the scan responses from reachable devices
 
     """
-    split_args = args.split()
-    enable = int(split_args[0]) if len(split_args) > 0 else 1
-    filter_dups = int(split_args[1]) if len(split_args) > 1 else 1
-    set_scan_enable = HCI_Hdr(type=1) / HCI_Command_Hdr(
-        opcode=0x200c) / HCI_Cmd_LE_Set_Scan_Enable(
+        split_args = args.split()
+        enable = int(split_args[0]) if len(split_args) > 0 else 1
+        filter_dups = int(split_args[1]) if len(split_args) > 1 else 1
+        set_scan_parameters = HCI_Hdr(type=1) / HCI_Command_Hdr(opcode=0x200b) / HCI_Cmd_LE_Set_Scan_Parameters(type=1)
+        print('Tx: ' + set_scan_parameters.__repr__())
+        self._hci.send(set_scan_parameters)
+        set_scan_enable = HCI_Hdr(type=1) / HCI_Command_Hdr(opcode=0x200c) / HCI_Cmd_LE_Set_Scan_Enable(
             enable=enable, filter_dups=filter_dups)
-    print('Tx: ' + set_scan_enable.__repr__())
-    self._hci.send(set_scan_enable)
+        print('Tx: ' + set_scan_enable.__repr__())
+        self._hci.send(set_scan_enable)
 
-  def do_scan(self, args):
-    """Arguments: timeout (seconds), max_results Print the scan responses from reachable devices
+    def do_scan(self, args):
+        """Arguments: timeout (seconds), max_results Print the scan responses from reachable devices
 
     """
-    split_args = args.split()
-    scan_time = int(split_args[0]) if len(split_args) > 0 else 0
-    max_responses = int(split_args[1]) if len(split_args) > 1 else 0
-    num_responses = 0
-    inquiry = HCI_Hdr(type='Command') / HCI_Command_Hdr(
-        opcode=0x0401) / HCI_Cmd_Inquiry(
+        split_args = args.split()
+        scan_time = int(split_args[0]) if len(split_args) > 0 else 0
+        max_responses = int(split_args[1]) if len(split_args) > 1 else 0
+        num_responses = 0
+        inquiry = HCI_Hdr(type='Command') / HCI_Command_Hdr(opcode=0x0401) / HCI_Cmd_Inquiry(
             length=scan_time, max_responses=max_responses)
-    print('Tx: ' + inquiry.__repr__())
-    self._hci.send(inquiry)
+        print('Tx: ' + inquiry.__repr__())
+        self._hci.send(inquiry)
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_wait(self, args):
+        """Arguments: time in seconds (float).
+    """
+        sleep_time = float(args.split()[0])
+        time.sleep(sleep_time)
+
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._hci.tell_rx_thread_to_quit()
-    self._hci.close()
-    print('Goodbye.')
-    return True
+        self._hci.tell_rx_thread_to_quit()
+        self._hci.close()
+        print('Goodbye.')
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
 
 def main(argv):
-  if len(argv) != 2:
-    print('Usage: python hci_socket.py [port]')
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print('Error parsing port.')
-  else:
+    if len(argv) != 2:
+        print('Usage: python hci_socket.py [port]')
+        return
     try:
-      hci = HCISocket(port)
-    except socket.error as e:
-      print('Error connecting to socket: %s' % e)
-    except:
-      print('Error creating (check arguments).')
+        port = int(argv[1])
+    except ValueError:
+        print('Error parsing port.')
     else:
-      hci_shell = HCIShell(hci)
-      hci_shell.prompt = '$ '
-      hci_shell.cmdloop('Welcome to the RootCanal HCI Console \n' +
-                        'Type \'help\' for more information.')
+        try:
+            hci = HCISocket(port)
+        except socket.error as e:
+            print('Error connecting to socket: %s' % e)
+        except:
+            print('Error creating (check arguments).')
+        else:
+            hci_shell = HCIShell(hci)
+            hci_shell.prompt = '$ '
+            hci_shell.cmdloop('Welcome to the RootCanal HCI Console \n' + 'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py b/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py
index c7b3045..d1268e9 100644
--- a/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py
+++ b/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py
@@ -69,124 +69,127 @@
 import struct
 import sys
 
+
 class LinkLayerSocket(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    print('port = ' + port)
-    self.done_ = False
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(('localhost', port))
-    # Should it be a non-blocking socket?
-    # self._socket.setblocking(0)
-    self.packets_ = queue.Queue()
-    self.rx_thread_ = threading.Thread(target=self.rx_thread_body)
-    self.rx_thread_.start()
+    def __init__(self, port):
+        print('port = ' + port)
+        self.done_ = False
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(('localhost', port))
+        # Should it be a non-blocking socket?
+        # self._socket.setblocking(0)
+        self.packets_ = queue.Queue()
+        self.rx_thread_ = threading.Thread(target=self.rx_thread_body)
+        self.rx_thread_.start()
 
-  def rx_bytes(self, size):
-    while not self.done_:
-      raw_bytes = b''
-      while len(raw_bytes) < size and not self.done_:
-        more_raw_bytes = self._socket.recv(min(size - len(raw_bytes), 2048))
-        if more_raw_bytes:
-          raw_bytes += more_raw_bytes
-      return raw_bytes
+    def rx_bytes(self, size):
+        while not self.done_:
+            raw_bytes = b''
+            while len(raw_bytes) < size and not self.done_:
+                more_raw_bytes = self._socket.recv(min(size - len(raw_bytes), 2048))
+                if more_raw_bytes:
+                    raw_bytes += more_raw_bytes
+            return raw_bytes
 
-  def rx_thread_body(self):
-    while not self.done_:
-      payload_length = 0
-      # Read the size (4B), the type (1B), and the addresses (2*6B)
-      header = self.rx_bytes(17)
-      if not header:
-        continue
-      payload_length = header[0]
-      payload_length |= header[1] << 8
-      payload_length |= header[2] << 16
-      payload_length |= header[3] << 24
-      print('Rx: type_byte ' + hex(header[4]))
-      print('Rx: from ' + hex(header[5]) + ':' + hex(header[6]) + ':' + hex(header[7]) + ':' + hex(header[8]) + ':' + hex(header[9]) + ':' + hex(header[10]))
-      print('Rx: to ' + hex(header[11]) + ':' + hex(header[12]) + ':' + hex(header[13]) + ':' + hex(header[14]) + ':' + hex(header[15]) + ':' + hex(header[16]))
-      # Read the Payload
-      payload = self.rx_bytes(payload_length) if payload_length != 0 else b''
-      packet_bytes = header + payload
-      self.packets_.put(packet_bytes)
+    def rx_thread_body(self):
+        while not self.done_:
+            payload_length = 0
+            # Read the size (4B), the type (1B), and the addresses (2*6B)
+            header = self.rx_bytes(17)
+            if not header:
+                continue
+            payload_length = header[0]
+            payload_length |= header[1] << 8
+            payload_length |= header[2] << 16
+            payload_length |= header[3] << 24
+            print('Rx: type_byte ' + hex(header[4]))
+            print('Rx: from ' + hex(header[5]) + ':' + hex(header[6]) + ':' + hex(header[7]) + ':' + hex(header[8]) +
+                  ':' + hex(header[9]) + ':' + hex(header[10]))
+            print('Rx: to ' + hex(header[11]) + ':' + hex(header[12]) + ':' + hex(header[13]) + ':' + hex(header[14]) +
+                  ':' + hex(header[15]) + ':' + hex(header[16]))
+            # Read the Payload
+            payload = self.rx_bytes(payload_length) if payload_length != 0 else b''
+            packet_bytes = header + payload
+            self.packets_.put(packet_bytes)
 
-  def get_packet(self):
-    if self.packets_.empty():
-      return False
-    return self.packets_.get()
+    def get_packet(self):
+        if self.packets_.empty():
+            return False
+        return self.packets_.get()
 
-  def send_binary(self, args):
-    joined_args = ''.join(arg for arg in args)
-    print(joined_args)
-    packet = binascii.a2b_hex(joined_args)
-    if self._done:
-      return
-    self._connection.send(packet)
+    def send_binary(self, args):
+        joined_args = ''.join(arg for arg in args)
+        print(joined_args)
+        packet = binascii.a2b_hex(joined_args)
+        if self._done:
+            return
+        self._connection.send(packet)
 
-  def tell_rx_thread_to_quit(self):
-    self.done_ = True
-    self.rx_thread_.join()
+    def tell_rx_thread_to_quit(self):
+        self.done_ = True
+        self.rx_thread_.join()
 
 
 class LinkLayerShell(cmd.Cmd):
-  """Shell for sending binary data to a port.
+    """Shell for sending binary data to a port.
 
   """
 
-  def __init__(self, link_layer):
-    cmd.Cmd.__init__(self)
-    self._link_layer = link_layer
+    def __init__(self, link_layer):
+        cmd.Cmd.__init__(self)
+        self._link_layer = link_layer
 
-  def do_send(self, args):
-    """Arguments: binary representation of a packet.
+    def do_send(self, args):
+        """Arguments: binary representation of a packet.
 
     """
-    self._link_layer.send_binary(args.split())
+        self._link_layer.send_binary(args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._link_layer.tell_rx_thread_to_quit()
-    self._link_layer.close()
-    print('Goodbye.')
-    return True
+        self._link_layer.tell_rx_thread_to_quit()
+        self._link_layer.close()
+        print('Goodbye.')
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
 
 def main(argv):
-  if len(argv) != 2:
-    print('Usage: python link_layer_socket.py [port]')
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print('Error parsing port.')
-  else:
+    if len(argv) != 2:
+        print('Usage: python link_layer_socket.py [port]')
+        return
     try:
-      link_layer = LinkLayerSocket(port)
-    except socket.error as e:
-      print('Error connecting to socket: %s' % e)
-    except:
-      print('Error creating (check arguments).')
+        port = int(argv[1])
+    except ValueError:
+        print('Error parsing port.')
     else:
-      link_layer_shell = LinkLayerShell(link_layer)
-      link_layer_shell.prompt = '$ '
-      link_layer_shell.cmdloop('Welcome to the RootCanal LinkLayer Console \n' +
-                        'Type \'help\' for more information.')
+        try:
+            link_layer = LinkLayerSocket(port)
+        except socket.error as e:
+            print('Error connecting to socket: %s' % e)
+        except:
+            print('Error creating (check arguments).')
+        else:
+            link_layer_shell = LinkLayerShell(link_layer)
+            link_layer_shell.prompt = '$ '
+            link_layer_shell.cmdloop('Welcome to the RootCanal LinkLayer Console \n' +
+                                     'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/Readme.txt b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/Readme.txt
new file mode 100644
index 0000000..2c25da7
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/Readme.txt
@@ -0,0 +1,11 @@
+This test:
+
+- Starts RootCanal
+- Adds scripted beacons
+- Sets the permissions for some of the files
+- Dumps the proto output for manual verification
+
+
+cd $ANDROID_BUILD_TOP/system/bt/vendor_libs/test_vendor_lib
+. scripts/scripted_beacon_test/run_test.sh
+
diff --git a/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/add_beacons b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/add_beacons
new file mode 100644
index 0000000..e6ebf68
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/add_beacons
@@ -0,0 +1,10 @@
+wait 1
+add scripted_beacon be:ac:01:55:00:01 scripts/scripted_beacon_test/test_events.pb /tmp/logs/scripted_beacon_test/beacon.pb
+add_device_to_phy 1 1
+add scripted_beacon be:ac:01:55:00:02 scripts/scripted_beacon_test/no_permission.pb /tmp/logs/scripted_beacon_test/no_permission.pb
+add_device_to_phy 2 1
+add scripted_beacon be:ac:01:55:00:02 scripts/scripted_beacon_test/grant_permission.pb /tmp/logs/scripted_beacon_test/grant_permission.pb
+add_device_to_phy 3 1
+list
+wait 10
+quit
diff --git a/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/grant_permission.pb b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/grant_permission.pb
new file mode 100644
index 0000000..0e065d7
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/grant_permission.pb
@@ -0,0 +1,21 @@
+
+
+oýoýè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoý	ªªªªªªªªªªªªªªª	ªªª	è
\ No newline at end of file
diff --git a/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/no_permission.pb b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/no_permission.pb
new file mode 100644
index 0000000..0e065d7
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/no_permission.pb
@@ -0,0 +1,21 @@
+
+
+oýoýè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoý	ªªªªªªªªªªªªªªª	ªªª	è
\ No newline at end of file
diff --git a/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/run_test.sh b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/run_test.sh
new file mode 100644
index 0000000..3cededb
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/run_test.sh
@@ -0,0 +1,17 @@
+
+mkdir -p /tmp/logs/scripted_beacon_test/
+chmod 200 scripts/scripted_beacon_test/no_permission.pb
+chmod 200 scripts/scripted_beacon_test/grant_permission.pb
+# ls -l scripts/scripted_beacon_test/*.pb
+$ANDROID_BUILD_TOP/out/host/linux-x86/bin/root-canal 2> /tmp/logs/scripted_beacon_test/root_canal.log &
+sleep 1
+python3 scripts/test_channel.py 6401 < scripts/scripted_beacon_test/add_beacons > /tmp/logs/scripted_beacon_test/test_channel.log &
+python3 scripts/hci_socket.py 6402 < scripts/scripted_beacon_test/start_scan > /tmp/logs/scripted_beacon_test/hci_device.log &
+sleep 5
+chmod 640 scripts/scripted_beacon_test/grant_permission.pb
+# ls -l scripts/scripted_beacon_test/*.pb
+sleep 15
+echo "Done"
+chmod 640 scripts/scripted_beacon_test/no_permission.pb
+# ls -l scripts/scripted_beacon_test/*.pb
+gqui /tmp/logs/scripted_beacon_test/*.pb
diff --git a/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/start_scan b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/start_scan
new file mode 100644
index 0000000..e345835
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/start_scan
@@ -0,0 +1,3 @@
+le_scan 1
+wait 10
+quit
diff --git a/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/test_events.pb b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/test_events.pb
new file mode 100644
index 0000000..0e065d7
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/scripts/scripted_beacon_test/test_events.pb
@@ -0,0 +1,21 @@
+
+
+oýoýè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoýªªªªªªªªªªªªªªªªªªè
+,
+oýoý	ªªªªªªªªªªªªªªª	ªªª	è
\ No newline at end of file
diff --git a/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py b/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py
index cadeab3..72f3e18 100644
--- a/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py
+++ b/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py
@@ -75,146 +75,145 @@
 
 # Used to generate fake device names and addresses during discovery.
 def generate_random_name():
-  return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
-    string.digits) for _ in range(DEVICE_NAME_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
+      string.digits) for _ in range(DEVICE_NAME_LENGTH))
 
 
 def generate_random_address():
-  return ''.join(random.SystemRandom().choice(string.digits) for _ in \
-    range(DEVICE_ADDRESS_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.digits) for _ in \
+      range(DEVICE_ADDRESS_LENGTH))
 
 
 class Connection(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(('localhost', port))
-    self._socket.setblocking(0)
+    def __init__(self, port):
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(('localhost', port))
+        self._socket.setblocking(0)
 
-  def close(self):
-    self._socket.close()
+    def close(self):
+        self._socket.close()
 
-  def send(self, data):
-    self._socket.sendall(data)
+    def send(self, data):
+        self._socket.sendall(data)
 
-  def receive(self, size):
-    return self._socket.recv(size)
+    def receive(self, size):
+        return self._socket.recv(size)
 
 
 class RawPort(object):
-  """Checks outgoing commands and sends them once verified.
+    """Checks outgoing commands and sends them once verified.
 
   Attributes:
     connection: The connection to the HCI port.
   """
 
-  def __init__(self, port):
-    self._connection = Connection(port)
-    self._closed = False
+    def __init__(self, port):
+        self._connection = Connection(port)
+        self._closed = False
 
-  def close(self):
-    self._connection.close()
-    self._closed = True
+    def close(self):
+        self._connection.close()
+        self._closed = True
 
-  def send_binary(self, args):
-    joined_args = ''.join(arg for arg in args)
-    print(joined_args)
-    packet = binascii.a2b_hex(joined_args)
-    if self._closed:
-      return
-    self._connection.send(packet)
-    received = self.receive_response()
-    received_bytes = bytearray(received)
-    print(raw(received_bytes))
+    def send_binary(self, args):
+        joined_args = ''.join(arg for arg in args)
+        print(joined_args)
+        packet = binascii.a2b_hex(joined_args)
+        if self._closed:
+            return
+        self._connection.send(packet)
+        received = self.receive_response()
+        received_bytes = bytearray(received)
+        print(raw(received_bytes))
 
-  def receive_response(self):
-    if self._closed:
-      return
-    size_chars = self._connection.receive(4)
-    if not size_chars:
-      print('Debug: No response')
-      return False
-    size_bytes = bytearray(size_chars)
-    response_size = 0
-    for i in range(0, len(size_chars) - 1):
-      response_size |= ord(size_chars[i]) << (8 * i)
-    response = self._connection.receive(response_size)
-    return response
+    def receive_response(self):
+        if self._closed:
+            return
+        size_chars = self._connection.receive(4)
+        if not size_chars:
+            print('Debug: No response')
+            return False
+        size_bytes = bytearray(size_chars)
+        response_size = 0
+        for i in range(0, len(size_chars) - 1):
+            response_size |= ord(size_chars[i]) << (8 * i)
+        response = self._connection.receive(response_size)
+        return response
 
-  def lint_command(self, name, args, name_size, args_size):
-    assert name_size == len(name) and args_size == len(args)
-    try:
-      name.encode('utf-8')
-      for arg in args:
-        arg.encode('utf-8')
-    except UnicodeError:
-      print('Unrecognized characters.')
-      raise
-    if name_size > 255 or args_size > 255:
-      raise ValueError  # Size must be encodable in one octet.
-    for arg in args:
-      if len(arg) > 255:
-        raise ValueError  # Size must be encodable in one octet.
+    def lint_command(self, name, args, name_size, args_size):
+        assert name_size == len(name) and args_size == len(args)
+        try:
+            name.encode('utf-8')
+            for arg in args:
+                arg.encode('utf-8')
+        except UnicodeError:
+            print('Unrecognized characters.')
+            raise
+        if name_size > 255 or args_size > 255:
+            raise ValueError  # Size must be encodable in one octet.
+        for arg in args:
+            if len(arg) > 255:
+                raise ValueError  # Size must be encodable in one octet.
 
 
 class RawPortShell(cmd.Cmd):
-  """Shell for sending binary data to a port.
+    """Shell for sending binary data to a port.
 
   """
 
-  def __init__(self, raw_port):
-    cmd.Cmd.__init__(self)
-    self._raw_port = raw_port
+    def __init__(self, raw_port):
+        cmd.Cmd.__init__(self)
+        self._raw_port = raw_port
 
-  def do_send(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_send(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._raw_port.send_binary(args.split())
+        self._raw_port.send_binary(args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._raw_port.close()
-    print('Goodbye.')
-    return True
+        self._raw_port.close()
+        print('Goodbye.')
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
 
 def main(argv):
-  if len(argv) != 2:
-    print('Usage: python raw_port.py [port]')
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print('Error parsing port.')
-  else:
+    if len(argv) != 2:
+        print('Usage: python raw_port.py [port]')
+        return
     try:
-      raw_port = RawPort(port)
-    except (socket.error, e):
-      print('Error connecting to socket: %s' % e)
-    except:
-      print('Error creating (check arguments).')
+        port = int(argv[1])
+    except ValueError:
+        print('Error parsing port.')
     else:
-      raw_port_shell = RawPortShell(raw_port)
-      raw_port_shell.prompt = '$ '
-      raw_port_shell.cmdloop('Welcome to the RootCanal Console \n' +
-                             'Type \'help\' for more information.')
+        try:
+            raw_port = RawPort(port)
+        except (socket.error, e):
+            print('Error connecting to socket: %s' % e)
+        except:
+            print('Error creating (check arguments).')
+        else:
+            raw_port_shell = RawPortShell(raw_port)
+            raw_port_shell.prompt = '$ '
+            raw_port_shell.cmdloop('Welcome to the RootCanal Console \n' + 'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/simple_link_layer_socket.py b/vendor_libs/test_vendor_lib/scripts/simple_link_layer_socket.py
index 982aee2..5ce2a35 100644
--- a/vendor_libs/test_vendor_lib/scripts/simple_link_layer_socket.py
+++ b/vendor_libs/test_vendor_lib/scripts/simple_link_layer_socket.py
@@ -61,137 +61,136 @@
 
 # Used to generate fake device names and addresses during discovery.
 def generate_random_name():
-  return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
-    string.digits) for _ in range(DEVICE_NAME_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
+      string.digits) for _ in range(DEVICE_NAME_LENGTH))
 
 
 def generate_random_address():
-  return ''.join(random.SystemRandom().choice(string.digits) for _ in \
-    range(DEVICE_ADDRESS_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.digits) for _ in \
+      range(DEVICE_ADDRESS_LENGTH))
 
 
 class Connection(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(('localhost', port))
-    self._socket.setblocking(0)
+    def __init__(self, port):
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(('localhost', port))
+        self._socket.setblocking(0)
 
-  def close(self):
-    self._socket.close()
+    def close(self):
+        self._socket.close()
 
-  def send(self, data):
-    self._socket.sendall(data)
+    def send(self, data):
+        self._socket.sendall(data)
 
-  def receive(self, size):
-    return self._socket.recv(size)
+    def receive(self, size):
+        return self._socket.recv(size)
 
 
 class RawPort(object):
-  """Checks outgoing commands and sends them once verified.
+    """Checks outgoing commands and sends them once verified.
 
   Attributes:
     connection: The connection to the HCI port.
   """
 
-  def __init__(self, port):
-    self._connection = Connection(port)
-    self._closed = False
+    def __init__(self, port):
+        self._connection = Connection(port)
+        self._closed = False
 
-  def close(self):
-    self._connection.close()
-    self._closed = True
+    def close(self):
+        self._connection.close()
+        self._closed = True
 
-  def send_binary(self, args):
-    joined_args = ''.join(arg for arg in args)
-    print(joined_args)
-    packet = binascii.a2b_hex(joined_args)
-    if self._closed:
-      return
-    self._connection.send(packet)
+    def send_binary(self, args):
+        joined_args = ''.join(arg for arg in args)
+        print(joined_args)
+        packet = binascii.a2b_hex(joined_args)
+        if self._closed:
+            return
+        self._connection.send(packet)
 
-  def receive_response(self):
-    if self._closed:
-      return
-    size_chars = self._connection.receive(4)
-    if not size_chars:
-      print('Debug: No response')
-      return False
-    size_bytes = bytearray(size_chars)
-    response_size = 0
-    for i in range(0, len(size_chars) - 1):
-      response_size |= ord(size_chars[i]) << (8 * i)
-    response = self._connection.receive(response_size)
-    return response
+    def receive_response(self):
+        if self._closed:
+            return
+        size_chars = self._connection.receive(4)
+        if not size_chars:
+            print('Debug: No response')
+            return False
+        size_bytes = bytearray(size_chars)
+        response_size = 0
+        for i in range(0, len(size_chars) - 1):
+            response_size |= ord(size_chars[i]) << (8 * i)
+        response = self._connection.receive(response_size)
+        return response
 
-  def lint_command(self, name, args, name_size, args_size):
-    assert name_size == len(name) and args_size == len(args)
-    try:
-      name.encode('utf-8')
-      for arg in args:
-        arg.encode('utf-8')
-    except UnicodeError:
-      print('Unrecognized characters.')
-      raise
-    if name_size > 255 or args_size > 255:
-      raise ValueError  # Size must be encodable in one octet.
-    for arg in args:
-      if len(arg) > 255:
-        raise ValueError  # Size must be encodable in one octet.
+    def lint_command(self, name, args, name_size, args_size):
+        assert name_size == len(name) and args_size == len(args)
+        try:
+            name.encode('utf-8')
+            for arg in args:
+                arg.encode('utf-8')
+        except UnicodeError:
+            print('Unrecognized characters.')
+            raise
+        if name_size > 255 or args_size > 255:
+            raise ValueError  # Size must be encodable in one octet.
+        for arg in args:
+            if len(arg) > 255:
+                raise ValueError  # Size must be encodable in one octet.
 
 
 class RawPortShell(cmd.Cmd):
-  """Shell for sending binary data to a port."""
+    """Shell for sending binary data to a port."""
 
-  def __init__(self, raw_port):
-    cmd.Cmd.__init__(self)
-    self._raw_port = raw_port
+    def __init__(self, raw_port):
+        cmd.Cmd.__init__(self)
+        self._raw_port = raw_port
 
-  def do_send(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str."""
-    self._raw_port.send_binary(args.split())
+    def do_send(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str."""
+        self._raw_port.send_binary(args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._raw_port.close()
-    print('Goodbye.')
-    return True
+        self._raw_port.close()
+        print('Goodbye.')
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr."""
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr."""
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
 
 def main(argv):
-  if len(argv) != 2:
-    print('Usage: python raw_port.py [port]')
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print('Error parsing port.')
-  else:
+    if len(argv) != 2:
+        print('Usage: python raw_port.py [port]')
+        return
     try:
-      raw_port = RawPort(port)
-    except (socket.error, e):
-      print('Error connecting to socket: %s' % e)
-    except:
-      print('Error creating (check arguments).')
+        port = int(argv[1])
+    except ValueError:
+        print('Error parsing port.')
     else:
-      raw_port_shell = RawPortShell(raw_port)
-      raw_port_shell.prompt = '$ '
-      raw_port_shell.cmdloop('Welcome to the RootCanal Console \n' +
-                             'Type \'help\' for more information.')
+        try:
+            raw_port = RawPort(port)
+        except (socket.error, e):
+            print('Error connecting to socket: %s' % e)
+        except:
+            print('Error creating (check arguments).')
+        else:
+            raw_port_shell = RawPortShell(raw_port)
+            raw_port_shell.prompt = '$ '
+            raw_port_shell.cmdloop('Welcome to the RootCanal Console \n' + 'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/simple_stack.py b/vendor_libs/test_vendor_lib/scripts/simple_stack.py
index 95d973a..5139c50 100644
--- a/vendor_libs/test_vendor_lib/scripts/simple_stack.py
+++ b/vendor_libs/test_vendor_lib/scripts/simple_stack.py
@@ -72,167 +72,166 @@
 
 
 class HCI_Cmd_Connect(Packet):
-  name = "Connect"
-  fields_desc = [
-      ByteEnumField("filter", 0, {0: "address"}),
-      LEShortField("packet_type", 8),
-      ByteEnumField("page_scan_repetition_mode", 0, {
-          0: "R0",
-          1: "R1",
-          2: "R2"
-      }),
-      ByteEnumField("page_scan_repetition_mode", 0, {0: "Reserved"}),
-      LEShortField("clock_offset", 0),
-      ByteEnumField("allow_role_switch", 0, {
-          0: "false",
-          1: "true"
-      }),
-  ]
+    name = "Connect"
+    fields_desc = [
+        ByteEnumField("filter", 0, {0: "address"}),
+        LEShortField("packet_type", 8),
+        ByteEnumField("page_scan_repetition_mode", 0, {
+            0: "R0",
+            1: "R1",
+            2: "R2"
+        }),
+        ByteEnumField("page_scan_repetition_mode", 0, {0: "Reserved"}),
+        LEShortField("clock_offset", 0),
+        ByteEnumField("allow_role_switch", 0, {
+            0: "false",
+            1: "true"
+        }),
+    ]
 
 
 class HCI_Cmd_Inquiry(Packet):
-  name = "Inquiry"
-  fields_desc = [
-      XByteField("LAP0", 0),
-      XByteField("LAP1", 0x8B),
-      XByteField("LAP2", 0x9E),
-      ByteField("length", 1),
-      ByteField("max_responses", 0),
-  ]
+    name = "Inquiry"
+    fields_desc = [
+        XByteField("LAP0", 0),
+        XByteField("LAP1", 0x8B),
+        XByteField("LAP2", 0x9E),
+        ByteField("length", 1),
+        ByteField("max_responses", 0),
+    ]
 
 
 """ END SCAPY stuff"""
 
 
 class Connection(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(("localhost", port))
+    def __init__(self, port):
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(("localhost", port))
 
-  def close(self):
-    self._socket.close()
+    def close(self):
+        self._socket.close()
 
-  def send(self, data):
-    self._socket.sendall(data)
+    def send(self, data):
+        self._socket.sendall(data)
 
-  def receive(self, size):
-    return self._socket.recv(size)
+    def receive(self, size):
+        return self._socket.recv(size)
 
 
 class RawPort(object):
-  """Converts outgoing packets to binary and sends them.
+    """Converts outgoing packets to binary and sends them.
 
   Attributes:
     connection: The connection to the HCI port.
   """
 
-  def __init__(self, port):
-    self._connection = Connection(port)
+    def __init__(self, port):
+        self._connection = Connection(port)
 
-  def close(self):
-    self._connection.close()
+    def close(self):
+        self._connection.close()
 
-  def send_binary(self, args):
-    joined_args = "".join(arg for arg in args)
-    print(joined_args)
-    packet = binascii.a2b_hex(joined_args)
-    self._connection.send(packet)
+    def send_binary(self, args):
+        joined_args = "".join(arg for arg in args)
+        print(joined_args)
+        packet = binascii.a2b_hex(joined_args)
+        self._connection.send(packet)
 
-  def receive_response(self):
-    ready_to_read, ready_to_write, in_error = \
-               select(
-                  [ self._connection._socket ],
-                  [ ],
-                  [ self._connection._socket ],
-                  1.5)
-    if len(in_error) > 0:
-      print("Error")
-      return False
-    if len(ready_to_read) > 0:
-      print("Ready to Read")
-      type_str = self._connection.receive(512)
-      print(len(type_str))
-      print(type_str)
-      return type_str
-    print("Returning false at the end")
-    return False
+    def receive_response(self):
+        ready_to_read, ready_to_write, in_error = \
+                   select(
+                      [ self._connection._socket ],
+                      [ ],
+                      [ self._connection._socket ],
+                      1.5)
+        if len(in_error) > 0:
+            print("Error")
+            return False
+        if len(ready_to_read) > 0:
+            print("Ready to Read")
+            type_str = self._connection.receive(512)
+            print(len(type_str))
+            print(type_str)
+            return type_str
+        print("Returning false at the end")
+        return False
 
 
 class RawPortShell(cmd.Cmd):
-  """Shell for sending binary data to a port.
+    """Shell for sending binary data to a port.
 
   """
 
-  def __init__(self, raw_port):
-    cmd.Cmd.__init__(self)
-    self._raw_port = raw_port
+    def __init__(self, raw_port):
+        cmd.Cmd.__init__(self)
+        self._raw_port = raw_port
 
-  def do_send(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_send(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._raw_port.send_binary(args.split())
+        self._raw_port.send_binary(args.split())
 
-  def do_scan(self, args):
-    """Arguments: timeout (seconds) Print the scan responses from reachable devices
+    def do_scan(self, args):
+        """Arguments: timeout (seconds) Print the scan responses from reachable devices
 
     """
-    self._raw_port.send_binary(args.split())
+        self._raw_port.send_binary(args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._raw_port.close()
-    print("Goodbye.")
-    return True
+        self._raw_port.close()
+        print("Goodbye.")
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
-  def postcmd(self, stop, line):
-    """Called after each command stop : whether we will stop after this command line : the previous input line Return True to stop, False to continue
+    def postcmd(self, stop, line):
+        """Called after each command stop : whether we will stop after this command line : the previous input line Return True to stop, False to continue
 
     """
-    if stop:
-      return True
-    response = self._raw_port.receive_response()
-    print(response)
-    return False
+        if stop:
+            return True
+        response = self._raw_port.receive_response()
+        print(response)
+        return False
 
 
 def main(argv):
-  if len(argv) != 2:
-    print("Usage: python raw_port.py [port]")
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print("Error parsing port.")
-  else:
+    if len(argv) != 2:
+        print("Usage: python raw_port.py [port]")
+        return
     try:
-      raw_port = RawPort(port)
-    except (socket.error, e):
-      print("Error connecting to socket: %s" % e)
-    except:
-      print("Error creating (check arguments).")
+        port = int(argv[1])
+    except ValueError:
+        print("Error parsing port.")
     else:
-      raw_port_shell = RawPortShell(raw_port)
-      raw_port_shell.prompt = "$ "
-      raw_port_shell.cmdloop("Welcome to the RootCanal Console \n" +
-                             'Type \'help\' for more information.')
+        try:
+            raw_port = RawPort(port)
+        except (socket.error, e):
+            print("Error connecting to socket: %s" % e)
+        except:
+            print("Error creating (check arguments).")
+        else:
+            raw_port_shell = RawPortShell(raw_port)
+            raw_port_shell.prompt = "$ "
+            raw_port_shell.cmdloop("Welcome to the RootCanal Console \n" + 'Type \'help\' for more information.')
 
 
 if __name__ == "__main__":
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/test_channel.py b/vendor_libs/test_vendor_lib/scripts/test_channel.py
index 447130b..161c2bb 100644
--- a/vendor_libs/test_vendor_lib/scripts/test_channel.py
+++ b/vendor_libs/test_vendor_lib/scripts/test_channel.py
@@ -42,6 +42,7 @@
 import string
 import struct
 import sys
+import time
 
 DEVICE_NAME_LENGTH = 6
 DEVICE_ADDRESS_LENGTH = 6
@@ -49,97 +50,96 @@
 
 # Used to generate fake device names and addresses during discovery.
 def generate_random_name():
-  return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
-    string.digits) for _ in range(DEVICE_NAME_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
+      string.digits) for _ in range(DEVICE_NAME_LENGTH))
 
 
 def generate_random_address():
-  return ''.join(random.SystemRandom().choice(string.digits) for _ in \
-    range(DEVICE_ADDRESS_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.digits) for _ in \
+      range(DEVICE_ADDRESS_LENGTH))
 
 
 class Connection(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(('localhost', port))
+    def __init__(self, port):
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(('localhost', port))
 
-  def close(self):
-    self._socket.close()
+    def close(self):
+        self._socket.close()
 
-  def send(self, data):
-    self._socket.sendall(data)
+    def send(self, data):
+        self._socket.sendall(data.encode())
 
-  def receive(self, size):
-    return self._socket.recv(size)
+    def receive(self, size):
+        return self._socket.recv(size)
 
 
 class TestChannel(object):
-  """Checks outgoing commands and sends them once verified.
+    """Checks outgoing commands and sends them once verified.
 
   Attributes:
     connection: The connection to the test vendor library that commands are sent
       on.
   """
 
-  def __init__(self, port):
-    self._connection = Connection(port)
-    self._closed = False
+    def __init__(self, port):
+        self._connection = Connection(port)
+        self._closed = False
 
-  def close(self):
-    self._connection.close()
-    self._closed = True
+    def close(self):
+        self._connection.close()
+        self._closed = True
 
-  def send_command(self, name, args):
-    name_size = len(name)
-    args_size = len(args)
-    self.lint_command(name, args, name_size, args_size)
-    encoded_name = chr(name_size) + name
-    encoded_args = chr(args_size) + ''.join(chr(len(arg)) + arg for arg in args)
-    command = encoded_name + encoded_args
-    if self._closed:
-      return
-    self._connection.send(command)
-    if name != 'CLOSE_TEST_CHANNEL':
-      print self.receive_response()
+    def send_command(self, name, args):
+        name_size = len(name)
+        args_size = len(args)
+        self.lint_command(name, args, name_size, args_size)
+        encoded_name = chr(name_size) + name
+        encoded_args = chr(args_size) + ''.join(chr(len(arg)) + arg for arg in args)
+        command = encoded_name + encoded_args
+        if self._closed:
+            return
+        self._connection.send(command)
+        if name != 'CLOSE_TEST_CHANNEL':
+            print(self.receive_response().decode())
 
-  def receive_response(self):
-    if self._closed:
-      return
-    size_chars = self._connection.receive(4)
-    size_bytes = bytearray(size_chars)
-    if not size_chars:
-      print 'No response, assuming that the connection is broken'
-      return False
-    response_size = 0
-    for i in range(0, len(size_chars) - 1):
-      response_size |= ord(size_chars[i]) << (8 * i)
-    response = self._connection.receive(response_size)
-    return response
+    def receive_response(self):
+        if self._closed:
+            return b'Closed'
+        size_chars = self._connection.receive(4)
+        size_bytes = bytearray(size_chars)
+        if not size_chars:
+            return b'No response, assuming that the connection is broken'
+        response_size = 0
+        for i in range(0, len(size_chars) - 1):
+            response_size |= (size_chars[i] << (8 * i))
+        response = self._connection.receive(response_size)
+        return response
 
-  def lint_command(self, name, args, name_size, args_size):
-    assert name_size == len(name) and args_size == len(args)
-    try:
-      name.encode('utf-8')
-      for arg in args:
-        arg.encode('utf-8')
-    except UnicodeError:
-      print 'Unrecognized characters.'
-      raise
-    if name_size > 255 or args_size > 255:
-      raise ValueError  # Size must be encodable in one octet.
-    for arg in args:
-      if len(arg) > 255:
-        raise ValueError  # Size must be encodable in one octet.
+    def lint_command(self, name, args, name_size, args_size):
+        assert name_size == len(name) and args_size == len(args)
+        try:
+            name.encode()
+            for arg in args:
+                arg.encode()
+        except UnicodeError:
+            print('Unrecognized characters.')
+            raise
+        if name_size > 255 or args_size > 255:
+            raise ValueError  # Size must be encodable in one octet.
+        for arg in args:
+            if len(arg) > 255:
+                raise ValueError  # Size must be encodable in one octet.
 
 
 class TestChannelShell(cmd.Cmd):
-  """Shell for sending test channel data to controller.
+    """Shell for sending test channel data to controller.
 
   Manages the test channel to the controller and defines a set of commands the
   user can send to the controller as well. These commands are processed parallel
@@ -150,138 +150,158 @@
     test_channel: The communication channel to send data to the controller.
   """
 
-  def __init__(self, test_channel):
-    cmd.Cmd.__init__(self)
-    self._test_channel = test_channel
+    def __init__(self, test_channel):
+        cmd.Cmd.__init__(self)
+        self._test_channel = test_channel
 
-  def do_add(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_add(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._test_channel.send_command('add', args.split())
+        self._test_channel.send_command('add', args.split())
 
-  def do_del(self, args):
-    """Arguments: device index Delete the device with the specified index.
+    def do_del(self, args):
+        """Arguments: device index Delete the device with the specified index.
 
     """
-    self._test_channel.send_command('del', args.split())
+        self._test_channel.send_command('del', args.split())
 
-  def do_add_phy(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_add_phy(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._test_channel.send_command('add_phy', args.split())
+        self._test_channel.send_command('add_phy', args.split())
 
-  def do_del_phy(self, args):
-    """Arguments: phy index Delete the phy with the specified index.
+    def do_del_phy(self, args):
+        """Arguments: phy index Delete the phy with the specified index.
 
     """
-    self._test_channel.send_command('del_phy', args.split())
+        self._test_channel.send_command('del_phy', args.split())
 
-  def do_add_device_to_phy(self, args):
-    """Arguments: device index phy index Add a new device of type dev_type_str.
+    def do_add_device_to_phy(self, args):
+        """Arguments: device index phy index Add a new device of type dev_type_str.
 
     """
-    self._test_channel.send_command('add_device_to_phy', args.split())
+        self._test_channel.send_command('add_device_to_phy', args.split())
 
-  def do_del_device_from_phy(self, args):
-    """Arguments: phy index Delete the phy with the specified index.
+    def do_del_device_from_phy(self, args):
+        """Arguments: phy index Delete the phy with the specified index.
 
     """
-    self._test_channel.send_command('del_device_from_phy', args.split())
+        self._test_channel.send_command('del_device_from_phy', args.split())
 
-  def do_add_remote(self, args):
-    """Arguments: dev_type_str Connect to a remote device at arg1@arg2.
+    def do_add_remote(self, args):
+        """Arguments: dev_type_str Connect to a remote device at arg1@arg2.
 
     """
-    self._test_channel.send_command('add_remote', args.split())
+        self._test_channel.send_command('add_remote', args.split())
 
-  def do_get(self, args):
-    """Arguments: dev_num attr_str Get the value of the attribute attr_str from device dev_num.
+    def do_get(self, args):
+        """Arguments: dev_num attr_str Get the value of the attribute attr_str from device dev_num.
 
     """
-    self._test_channel.send_command('get', args.split())
+        self._test_channel.send_command('get', args.split())
 
-  def do_set(self, args):
-    """Arguments: dev_num attr_str val Set the value of the attribute attr_str from device dev_num equal to val.
+    def do_set(self, args):
+        """Arguments: dev_num attr_str val Set the value of the attribute attr_str from device dev_num equal to val.
 
     """
-    self._test_channel.send_command('set', args.split())
+        self._test_channel.send_command('set', args.split())
 
-  def do_set_device_address(self, args):
-    """Arguments: dev_num addr Set the address of device dev_num equal to addr.
+    def do_set_device_address(self, args):
+        """Arguments: dev_num addr Set the address of device dev_num equal to addr.
 
     """
-    self._test_channel.send_command('set_device_address', args.split())
+        self._test_channel.send_command('set_device_address', args.split())
 
-  def do_list(self, args):
-    """Arguments: [dev_num [attr]] List the devices from the controller, optionally filtered by device and attr.
+    def do_list(self, args):
+        """Arguments: [dev_num [attr]] List the devices from the controller, optionally filtered by device and attr.
 
     """
-    self._test_channel.send_command('list', args.split())
+        self._test_channel.send_command('list', args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_set_timer_period(self, args):
+        """Arguments: period_ms Set the timer to fire every period_ms milliseconds
+    """
+        self._test_channel.send_command('set_timer_period', args.split())
+
+    def do_start_timer(self, args):
+        """Arguments: None. Start the timer.
+    """
+        self._test_channel.send_command('start_timer', args.split())
+
+    def do_stop_timer(self, args):
+        """Arguments: None. Stop the timer.
+    """
+        self._test_channel.send_command('stop_timer', args.split())
+
+    def do_wait(self, args):
+        """Arguments: time in seconds (float).
+    """
+        sleep_time = float(args.split()[0])
+        time.sleep(sleep_time)
+
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits the test channel.
     """
-    self._test_channel.send_command('CLOSE_TEST_CHANNEL', [])
-    self._test_channel.close()
-    print 'Goodbye.'
-    return True
+        self._test_channel.send_command('CLOSE_TEST_CHANNEL', [])
+        self._test_channel.close()
+        print('Goodbye.')
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
-    else:
-      self._test_channel.send_command('help', args.split())
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
+        else:
+            self._test_channel.send_command('help', args.split())
 
-  def preloop(self):
-    """Clear out the buffer
+    def preloop(self):
+        """Clear out the buffer
 
     """
-    response = self._test_channel.receive_response()
+        response = self._test_channel.receive_response()
 
-  #def postcmd(self, stop, line):
-  #"""
-  #Called after each command
-  #stop : whether we will stop after this command
-  #line : the previous input line
-  #Return True to stop, False to continue
-  #"""
-  #if stop:
-  #return True
-  #response = self._test_channel.receive_response()
-  #if not response:
-  #return True
-  #print response
-  #return False
+    #def postcmd(self, stop, line):
+    #"""
+    #Called after each command
+    #stop : whether we will stop after this command
+    #line : the previous input line
+    #Return True to stop, False to continue
+    #"""
+    #if stop:
+    #return True
+    #response = self._test_channel.receive_response()
+    #if not response:
+    #return True
+    #print response
+    #return False
 
 
 def main(argv):
-  if len(argv) != 2:
-    print 'Usage: python test_channel.py [port]'
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print 'Error parsing port.'
-  else:
+    if len(argv) != 2:
+        print('Usage: python test_channel.py [port]')
+        return
     try:
-      test_channel = TestChannel(port)
-    except socket.error, e:
-      print 'Error connecting to socket: %s' % e
-    except:
-      print 'Error creating test channel (check argument).'
+        port = int(argv[1])
+    except ValueError:
+        print('Error parsing port.')
     else:
-      test_channel_shell = TestChannelShell(test_channel)
-      test_channel_shell.prompt = '$ '
-      test_channel_shell.cmdloop('Welcome to the RootCanal Console \n' +
-                                 'Type \'help\' for more information.')
+        try:
+            test_channel = TestChannel(port)
+        except socket.error as e:
+            print('Error connecting to socket: %s' % e)
+        except:
+            print('Error creating test channel (check argument).')
+        else:
+            test_channel_shell = TestChannelShell(test_channel)
+            test_channel_shell.prompt = '$ '
+            test_channel_shell.cmdloop('Welcome to the RootCanal Console \n' + 'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)