Merge "SCO: Ignore close event at listen state"
diff --git a/audio_hal_interface/le_audio_software.cc b/audio_hal_interface/le_audio_software.cc
index 98604ae..39e3a52 100644
--- a/audio_hal_interface/le_audio_software.cc
+++ b/audio_hal_interface/le_audio_software.cc
@@ -116,33 +116,13 @@
 
   void MetadataChanged(const source_metadata_t& source_metadata) {
     auto track_count = source_metadata.track_count;
-    auto tracks = source_metadata.tracks;
-    LOG(INFO) << __func__ << ": " << track_count << " track(s) received";
 
     if (track_count == 0) {
       LOG(WARNING) << ", invalid number of metadata changed tracks";
       return;
     }
 
-    audio_usage_t usage = tracks->usage;
-    audio_content_type_t content_type = tracks->content_type;
-
-    /* TODO what stack should do with more than one track ? */
-    if (track_count > 1) {
-      LOG(WARNING) << __func__ << ", can't handle multiple tracks metadata, "
-                   << "count: " << track_count << " track(s) received";
-      return;
-    }
-
-    while (track_count) {
-      VLOG(1) << __func__ << ": usage=" << tracks->usage
-              << ", content_type=" << tracks->content_type
-              << ", gain=" << tracks->gain;
-      --track_count;
-      ++tracks;
-    }
-
-    stream_cb_.on_metadata_update_(usage, content_type);
+    stream_cb_.on_metadata_update_(source_metadata);
   }
 
   void ResetPresentationPosition() {
diff --git a/audio_hal_interface/le_audio_software.h b/audio_hal_interface/le_audio_software.h
index 17356d7..d595e6d 100644
--- a/audio_hal_interface/le_audio_software.h
+++ b/audio_hal_interface/le_audio_software.h
@@ -43,7 +43,7 @@
 struct StreamCallbacks {
   std::function<bool(bool start_media_task)> on_resume_;
   std::function<bool(void)> on_suspend_;
-  std::function<bool(audio_usage_t, audio_content_type_t)> on_metadata_update_;
+  std::function<bool(const source_metadata_t&)> on_metadata_update_;
 };
 
 class LeAudioClientInterface {
diff --git a/bta/Android.bp b/bta/Android.bp
index 819cd21..0242c8a 100644
--- a/bta/Android.bp
+++ b/bta/Android.bp
@@ -184,7 +184,6 @@
         "system/bt/gd",
     ],
     generated_headers: [
-        "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
@@ -381,10 +380,12 @@
         "system/bt/stack/include",
     ],
     srcs : [
+        ":TestMockOsi",
         "gatt/database.cc",
         "gatt/database_builder.cc",
         "test/common/bta_gatt_api_mock.cc",
         "test/common/bta_gatt_queue_mock.cc",
+        "test/common/mock_csis_client.cc",
         "test/common/btm_api_mock.cc",
         "vc/devices_test.cc",
         "vc/device.cc",
@@ -400,6 +401,7 @@
         "libgmock",
         "libbt-common",
         "libbt-protos-lite",
+        "libosi"
     ],
     sanitize: {
         cfi: false,
diff --git a/bta/csis/csis_client.cc b/bta/csis/csis_client.cc
index d7202be..c1a3f57 100644
--- a/bta/csis/csis_client.cc
+++ b/bta/csis/csis_client.cc
@@ -400,6 +400,21 @@
     if (cb) std::move(cb).Run(group_id, lock, status);
   }
 
+  std::vector<RawAddress> GetDeviceList(int group_id) override {
+    std::vector<RawAddress> result;
+    auto csis_group = FindCsisGroup(group_id);
+
+    if (!csis_group || csis_group->IsEmpty()) return result;
+
+    auto csis_device = csis_group->GetFirstDevice();
+    while (csis_device) {
+      result.push_back(csis_device->addr);
+      csis_device = csis_group->GetNextDevice(csis_device);
+    }
+
+    return result;
+  }
+
   void LockGroup(int group_id, bool lock, CsisLockCb cb) override {
     if (lock)
       DLOG(INFO) << __func__ << " Locking group: " << int(group_id);
@@ -455,7 +470,6 @@
       return;
     }
 
-    csis_group->SortByCsisRank();
     csis_group->SetTargetLockState(new_lock_state, std::move(cb));
 
     if (lock) {
@@ -986,6 +1000,8 @@
     }
 
     csis_instance->SetRank((value[0]));
+    auto csis_group = FindCsisGroup(csis_instance->GetGroupId());
+    csis_group->SortByCsisRank();
   }
 
   void OnCsisObserveCompleted(void) {
diff --git a/bta/hf_client/bta_hf_client_at.cc b/bta/hf_client/bta_hf_client_at.cc
index 721bbbc..f2fcf82 100644
--- a/bta/hf_client/bta_hf_client_at.cc
+++ b/bta/hf_client/bta_hf_client_at.cc
@@ -2132,6 +2132,17 @@
   for (i = 0; i < BTA_HF_CLIENT_AT_INDICATOR_COUNT; i++) {
     int sup = client_cb->at_cb.indicator_lookup[i] == -1 ? 0 : 1;
 
+/* If this value matches the position of SIGNAL in the indicators array,
+ * then hardcode disable signal strength indicators.
+ * indicator_lookup[i] points to the position in the bta_hf_client_indicators
+ * array defined at the top of this file */
+#ifdef BTA_HF_CLIENT_INDICATOR_SIGNAL_POS
+    if (client_cb->at_cb.indicator_lookup[i] ==
+        BTA_HF_CLIENT_INDICATOR_SIGNAL_POS) {
+      sup = 0;
+    }
+#endif
+
     at_len += snprintf(buf + at_len, sizeof(buf) - at_len, "%u,", sup);
   }
 
diff --git a/bta/include/bta_csis_api.h b/bta/include/bta_csis_api.h
index d3ede74..fe864f5 100644
--- a/bta/include/bta_csis_api.h
+++ b/bta/include/bta_csis_api.h
@@ -46,6 +46,7 @@
       const RawAddress& addr,
       bluetooth::Uuid uuid = bluetooth::groups::kGenericContextUuid) = 0;
   virtual void LockGroup(int group_id, bool lock, CsisLockCb cb) = 0;
+  virtual std::vector<RawAddress> GetDeviceList(int group_id) = 0;
 };
 }  // namespace csis
 }  // namespace bluetooth
diff --git a/bta/le_audio/client.cc b/bta/le_audio/client.cc
index f999736..54e6017 100644
--- a/bta/le_audio/client.cc
+++ b/bta/le_audio/client.cc
@@ -1252,6 +1252,9 @@
     const gatt::Service* pac_svc = nullptr;
     const gatt::Service* ase_svc = nullptr;
 
+    std::vector<uint16_t> csis_primary_handles;
+    uint16_t cas_csis_included_handle = 0;
+
     for (const gatt::Service& tmp : *services) {
       if (tmp.uuid == le_audio::uuid::kPublishedAudioCapabilityServiceUuid) {
         LOG(INFO) << "Found Audio Capability service, handle: "
@@ -1261,6 +1264,10 @@
         LOG(INFO) << "Found Audio Stream Endpoint service, handle: "
                   << loghex(tmp.handle);
         ase_svc = &tmp;
+      } else if (tmp.uuid == bluetooth::csis::kCsisServiceUuid) {
+        LOG(INFO) << "Found CSIS service, handle: " << loghex(tmp.handle)
+                  << " is primary? " << tmp.is_primary;
+        if (tmp.is_primary) csis_primary_handles.push_back(tmp.handle);
       } else if (tmp.uuid == le_audio::uuid::kCapServiceUuid) {
         LOG(INFO) << "Found CAP Service, handle: " << loghex(tmp.handle);
 
@@ -1269,7 +1276,7 @@
           if (included_srvc.uuid == bluetooth::csis::kCsisServiceUuid) {
             LOG(INFO) << __func__ << " CSIS included into CAS";
             if (bluetooth::csis::CsisClient::IsCsisClientRunning())
-              leAudioDevice->csis_member_ = true;
+              cas_csis_included_handle = included_srvc.start_handle;
 
             break;
           }
@@ -1277,6 +1284,15 @@
       }
     }
 
+    /* Check if CAS includes primary CSIS service */
+    if (!csis_primary_handles.empty() && cas_csis_included_handle) {
+      auto iter =
+          std::find(csis_primary_handles.begin(), csis_primary_handles.end(),
+                    cas_csis_included_handle);
+      if (iter != csis_primary_handles.end())
+        leAudioDevice->csis_member_ = true;
+    }
+
     if (!pac_svc || !ase_svc) {
       LOG(ERROR) << "No mandatory le audio services found";
 
@@ -2250,12 +2266,24 @@
     }
 
     if (audio_sink_ready_to_receive) {
+      LOG(INFO) << __func__ << " audio_sink_ready_to_receive";
       audio_source_ready_to_send = true;
       /* If signalling part is completed trigger start reveivin audio here,
        * otherwise it'll be called on group streaming state callback
        */
       if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)
         StartSendingAudio(active_group_id_);
+    } else {
+      /* Ask framework to come back later */
+      DLOG(INFO) << __func__ << " active_group_id: " << active_group_id_ << "\n"
+                 << " audio_sink_ready_to_receive: "
+                 << audio_sink_ready_to_receive << "\n"
+                 << " audio_source_ready_to_send:" << audio_source_ready_to_send
+                 << "\n"
+                 << " current_context_type_: "
+                 << static_cast<int>(current_context_type_) << "\n"
+                 << " group exist? " << (group ? " yes " : " no ") << "\n";
+      CancelStreamingRequest();
     }
   }
 
@@ -2308,54 +2336,77 @@
     }
   }
 
-  void OnAudioMetadataUpdate(audio_usage_t usage,
-                             audio_content_type_t content_type) {
-    LOG(INFO) << __func__ << ", content_type = "
-              << audio_content_type_to_string(content_type)
-              << ", usage = " << audio_usage_to_string(usage);
-
-    LeAudioContextType new_context = LeAudioContextType::RFU;
-
+  LeAudioContextType AudioContentToLeAudioContext(
+      audio_content_type_t content_type, audio_usage_t usage) {
     switch (content_type) {
       case AUDIO_CONTENT_TYPE_SPEECH:
-        new_context = LeAudioContextType::CONVERSATIONAL;
-        break;
+        return LeAudioContextType::CONVERSATIONAL;
       case AUDIO_CONTENT_TYPE_MUSIC:
       case AUDIO_CONTENT_TYPE_MOVIE:
       case AUDIO_CONTENT_TYPE_SONIFICATION:
-        new_context = LeAudioContextType::MEDIA;
-        break;
+        return LeAudioContextType::MEDIA;
       default:
         break;
     }
 
     /* Context is not clear, consider also usage of stream */
-    if (new_context == LeAudioContextType::RFU) {
-      switch (usage) {
-        case AUDIO_USAGE_VOICE_COMMUNICATION:
-          new_context = LeAudioContextType::CONVERSATIONAL;
-          break;
-        case AUDIO_USAGE_GAME:
-          new_context = LeAudioContextType::GAME;
-          break;
-        case AUDIO_USAGE_NOTIFICATION:
-          new_context = LeAudioContextType::NOTIFICATIONS;
-          break;
-        case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
-          new_context = LeAudioContextType::RINGTONE;
-          break;
-        case AUDIO_USAGE_ALARM:
-          new_context = LeAudioContextType::ALERTS;
-          break;
-        case AUDIO_USAGE_EMERGENCY:
-          new_context = LeAudioContextType::EMERGENCYALARM;
-          break;
-        default:
-          new_context = LeAudioContextType::MEDIA;
-          break;
-      }
+    switch (usage) {
+      case AUDIO_USAGE_VOICE_COMMUNICATION:
+        return LeAudioContextType::CONVERSATIONAL;
+      case AUDIO_USAGE_GAME:
+        return LeAudioContextType::GAME;
+      case AUDIO_USAGE_NOTIFICATION:
+        return LeAudioContextType::NOTIFICATIONS;
+      case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+        return LeAudioContextType::RINGTONE;
+      case AUDIO_USAGE_ALARM:
+        return LeAudioContextType::ALERTS;
+      case AUDIO_USAGE_EMERGENCY:
+        return LeAudioContextType::EMERGENCYALARM;
+      default:
+        break;
     }
 
+    return LeAudioContextType::MEDIA;
+  }
+
+  LeAudioContextType ChooseContextType(
+      std::vector<LeAudioContextType>& available_contents) {
+    /* Mini policy. Voice is prio 1, media is prio 2 */
+    auto iter = find(available_contents.begin(), available_contents.end(),
+                     LeAudioContextType::CONVERSATIONAL);
+    if (iter != available_contents.end())
+      return LeAudioContextType::CONVERSATIONAL;
+
+    iter = find(available_contents.begin(), available_contents.end(),
+                LeAudioContextType::MEDIA);
+    if (iter != available_contents.end()) return LeAudioContextType::MEDIA;
+
+    /*TODO do something smarter here */
+    return available_contents[0];
+  }
+
+  void OnAudioMetadataUpdate(const source_metadata_t& source_metadata) {
+    auto tracks = source_metadata.tracks;
+    auto track_count = source_metadata.track_count;
+
+    std::vector<LeAudioContextType> contexts;
+
+    while (track_count) {
+      DLOG(INFO) << __func__ << ": usage=" << tracks->usage
+                 << ", content_type=" << tracks->content_type
+                 << ", gain=" << tracks->gain;
+
+      auto new_context =
+          AudioContentToLeAudioContext(tracks->content_type, tracks->usage);
+      contexts.push_back(new_context);
+
+      --track_count;
+      ++tracks;
+    }
+
+    auto new_context = ChooseContextType(contexts);
+
     auto group = aseGroups_.FindById(active_group_id_);
     if (!group) {
       LOG(ERROR) << __func__
@@ -2372,17 +2423,24 @@
 
       std::optional<LeAudioCodecConfiguration> source_configuration =
           group->GetCodecConfigurationByDirection(
-              current_context_type_, le_audio::types::kLeAudioDirectionSink);
+              new_context, le_audio::types::kLeAudioDirectionSink);
+
       std::optional<LeAudioCodecConfiguration> sink_configuration =
           group->GetCodecConfigurationByDirection(
-              current_context_type_, le_audio::types::kLeAudioDirectionSource);
+              new_context, le_audio::types::kLeAudioDirectionSource);
 
-      if (source_configuration &&
-          (*source_configuration != current_source_codec_config))
+      if ((source_configuration &&
+           (*source_configuration != current_source_codec_config)) ||
+          (sink_configuration &&
+           (*sink_configuration != current_sink_codec_config))) {
+        do_in_main_thread(
+            FROM_HERE, base::Bind(&LeAudioClientImpl::UpdateCurrentHalSessions,
+                                  base::Unretained(instance), group->group_id_,
+                                  new_context));
+        current_context_type_ = new_context;
+        GroupStop(group->group_id_);
         return;
-      if (sink_configuration &&
-          (*sink_configuration != current_sink_codec_config))
-        return;
+      }
 
       /* Configuration is the same for new context, just will do update
        * metadata of stream
@@ -2711,10 +2769,10 @@
     do_resume_promise.set_value();
   }
 
-  void OnAudioMetadataUpdate(std::promise<void> do_metadata_update_promise,
-                             audio_usage_t usage,
-                             audio_content_type_t content_type) override {
-    if (instance) instance->OnAudioMetadataUpdate(usage, content_type);
+  void OnAudioMetadataUpdate(
+      std::promise<void> do_metadata_update_promise,
+      const source_metadata_t& source_metadata) override {
+    if (instance) instance->OnAudioMetadataUpdate(source_metadata);
     do_metadata_update_promise.set_value();
   }
 };
diff --git a/bta/le_audio/client_audio.cc b/bta/le_audio/client_audio.cc
index e758350..dbbaf62 100644
--- a/bta/le_audio/client_audio.cc
+++ b/bta/le_audio/client_audio.cc
@@ -212,8 +212,8 @@
   return false;
 }
 
-bool le_audio_sink_on_metadata_update_req(audio_usage_t usage,
-                                          audio_content_type_t content_type) {
+bool le_audio_sink_on_metadata_update_req(
+    const source_metadata_t& source_metadata) {
   if (localAudioSinkReceiver == nullptr) {
     LOG(ERROR) << __func__ << ", audio receiver not started";
     return false;
@@ -227,8 +227,7 @@
       FROM_HERE,
       base::BindOnce(&LeAudioClientAudioSinkReceiver::OnAudioMetadataUpdate,
                      base::Unretained(localAudioSinkReceiver),
-                     std::move(do_update_metadata_promise), usage,
-                     content_type));
+                     std::move(do_update_metadata_promise), source_metadata));
 
   if (status == BT_STATUS_SUCCESS) {
     do_update_metadata_future.wait();
diff --git a/bta/le_audio/client_audio.h b/bta/le_audio/client_audio.h
index 1bca618..b71fb23 100644
--- a/bta/le_audio/client_audio.h
+++ b/bta/le_audio/client_audio.h
@@ -28,8 +28,8 @@
   virtual void OnAudioSuspend(std::promise<void> do_suspend_promise) = 0;
   virtual void OnAudioResume(std::promise<void> do_resume_promise) = 0;
   virtual void OnAudioMetadataUpdate(
-      std::promise<void> do_update_metadata_promise, audio_usage_t usage,
-      audio_content_type_t content_type) = 0;
+      std::promise<void> do_update_metadata_promise,
+      const source_metadata_t& source_metadata) = 0;
 };
 class LeAudioClientAudioSourceReceiver {
  public:
diff --git a/bta/le_audio/client_audio_test.cc b/bta/le_audio/client_audio_test.cc
index 919f267..2eaadc6 100644
--- a/bta/le_audio/client_audio_test.cc
+++ b/bta/le_audio/client_audio_test.cc
@@ -189,7 +189,7 @@
               (override));
   MOCK_METHOD((void), OnAudioMetadataUpdate,
               (std::promise<void> do_update_metadata_promise,
-               audio_usage_t usage, audio_content_type_t content_type),
+               const source_metadata_t& source_metadata),
               (override));
 };
 
diff --git a/bta/le_audio/client_parser.cc b/bta/le_audio/client_parser.cc
index cb6936d..9a42e05 100644
--- a/bta/le_audio/client_parser.cc
+++ b/bta/le_audio/client_parser.cc
@@ -216,6 +216,8 @@
     return false;
   }
 
+  STREAM_TO_UINT8(rsp.cig_id, value);
+  STREAM_TO_UINT8(rsp.cis_id, value);
   STREAM_TO_UINT8(metadata_len, value);
   len -= kAseStatusTransMinLen;
 
@@ -227,7 +229,9 @@
   if (metadata_len > 0)
     rsp.metadata = std::vector<uint8_t>(value, value + metadata_len);
 
-  LOG(INFO) << __func__ << ", Status enabling/streaming/disabling metadata:"
+  LOG(INFO) << __func__ << ", Status enabling/streaming/disabling"
+            << "\n\tCIG: " << loghex(rsp.cig_id)
+            << "\n\tCIS: " << loghex(rsp.cis_id) << "\n\tMetadata: "
             << base::HexEncode(rsp.metadata.data(), rsp.metadata.size());
 
   return true;
diff --git a/bta/le_audio/client_parser.h b/bta/le_audio/client_parser.h
index f18d79d..159f452 100644
--- a/bta/le_audio/client_parser.h
+++ b/bta/le_audio/client_parser.h
@@ -115,8 +115,10 @@
   uint32_t pres_delay;
 };
 
-constexpr uint16_t kAseStatusTransMinLen = 1;
+constexpr uint16_t kAseStatusTransMinLen = 3;
 struct ase_transient_state_params {
+  uint8_t cig_id;
+  uint8_t cis_id;
   std::vector<uint8_t> metadata;
 };
 
diff --git a/bta/le_audio/client_parser_test.cc b/bta/le_audio/client_parser_test.cc
index 2927792..bd86e88 100644
--- a/bta/le_audio/client_parser_test.cc
+++ b/bta/le_audio/client_parser_test.cc
@@ -913,6 +913,10 @@
       0x01,
       // ASE State
       0x03,  // 'Enabling' state
+      // CIG_ID
+      0x03,
+      // CIS_ID
+      0x04,
       // Metadata length
       0x03,
       // Metadata
diff --git a/bta/le_audio/devices.cc b/bta/le_audio/devices.cc
index 1fd6f61..71c0de6 100644
--- a/bta/le_audio/devices.cc
+++ b/bta/le_audio/devices.cc
@@ -543,6 +543,7 @@
         active_contexts_has_been_modified = true;
       } else {
         /* Configuration is the same */
+        contexts |= type_set;
         continue;
       }
     }
@@ -951,17 +952,7 @@
     ase->max_sdu_size = codec_spec_caps::GetAudioChannelCounts(
                             ase->codec_config.audio_channel_allocation) *
                         ase->codec_config.octets_per_codec_frame;
-
-    /* Append additional metadata */
-    std::vector<uint8_t> metadata;
-    metadata.resize(3 + 1);
-    uint8_t* metadata_buf = metadata.data();
-    UINT8_TO_STREAM(metadata_buf, 3);
-    UINT8_TO_STREAM(metadata_buf,
-                    types::kLeAudioMetadataTypeStreamingAudioContext);
-    UINT16_TO_STREAM(metadata_buf, static_cast<uint16_t>(context_type));
-
-    ase->metadata = std::move(metadata);
+    ase->metadata = GetMetadata(context_type);
 
     DLOG(INFO) << __func__ << " device=" << address_
                << ", activated ASE id=" << +ase->id
@@ -1100,6 +1091,16 @@
   return group_config;
 }
 
+bool LeAudioDeviceGroup::IsMetadataChanged(
+    types::LeAudioContextType context_type) {
+  for (auto* leAudioDevice = GetFirstActiveDevice(); leAudioDevice;
+       leAudioDevice = GetNextActiveDevice(leAudioDevice)) {
+    if (leAudioDevice->IsMetadataChanged(context_type)) return true;
+  }
+
+  return false;
+}
+
 types::LeAudioContextType LeAudioDeviceGroup::GetCurrentContextType(void) {
   return active_context_type_;
 }
@@ -1618,6 +1619,25 @@
   }
 }
 
+std::vector<uint8_t> LeAudioDevice::GetMetadata(
+    LeAudioContextType context_type) {
+  std::vector<uint8_t> metadata;
+
+  AppendMetadataLtvEntryForStreamingContext(metadata, context_type);
+  AppendMetadataLtvEntryForCcidList(metadata, context_type);
+
+  return std::move(metadata);
+}
+
+bool LeAudioDevice::IsMetadataChanged(types::LeAudioContextType context_type) {
+  for (auto* ase = this->GetFirstActiveAse(); ase;
+       ase = this->GetNextActiveAse(ase)) {
+    if (this->GetMetadata(context_type) != ase->metadata) return true;
+  }
+
+  return false;
+}
+
 LeAudioDeviceGroup* LeAudioDeviceGroups::Add(int group_id) {
   /* Get first free group id */
   if (FindById(group_id)) {
diff --git a/bta/le_audio/devices.h b/bta/le_audio/devices.h
index 554140b..6801dd1 100644
--- a/bta/le_audio/devices.h
+++ b/bta/le_audio/devices.h
@@ -140,6 +140,8 @@
                                             types::AudioContexts src_cont_val);
   void DeactivateAllAses(void);
   void Dump(int fd);
+  std::vector<uint8_t> GetMetadata(types::LeAudioContextType context_type);
+  bool IsMetadataChanged(types::LeAudioContextType context_type);
 
  private:
   types::AudioContexts avail_snk_contexts_;
@@ -250,6 +252,7 @@
   types::AudioContexts GetActiveContexts(void);
   std::optional<LeAudioCodecConfiguration> GetCodecConfigurationByDirection(
       types::LeAudioContextType group_context_type, uint8_t direction);
+  bool IsMetadataChanged(types::LeAudioContextType group_context_type);
 
   inline types::AseState GetState(void) const { return current_state_; }
   void SetState(types::AseState state) {
diff --git a/bta/le_audio/le_audio_client_test.cc b/bta/le_audio/le_audio_client_test.cc
index 2407864..7a0b318 100644
--- a/bta/le_audio/le_audio_client_test.cc
+++ b/bta/le_audio/le_audio_client_test.cc
@@ -927,9 +927,20 @@
 
   void UpdateMetadata(audio_usage_t usage, audio_content_type_t content_type) {
     std::promise<void> do_metadata_update_promise;
+
+    struct playback_track_metadata tracks_[2] = {
+        {AUDIO_USAGE_UNKNOWN, AUDIO_CONTENT_TYPE_UNKNOWN, 0},
+        {AUDIO_USAGE_UNKNOWN, AUDIO_CONTENT_TYPE_UNKNOWN, 0}};
+
+    source_metadata_t source_metadata = {.track_count = 1,
+                                         .tracks = &tracks_[0]};
+
+    tracks_[0].usage = usage;
+    tracks_[0].content_type = content_type;
+
     auto do_metadata_update_future = do_metadata_update_promise.get_future();
     audio_sink_receiver_->OnAudioMetadataUpdate(
-        std::move(do_metadata_update_promise), usage, content_type);
+        std::move(do_metadata_update_promise), source_metadata);
     do_metadata_update_future.wait();
   }
 
diff --git a/bta/le_audio/le_audio_types.cc b/bta/le_audio/le_audio_types.cc
index d8286e1..0c4d1af 100644
--- a/bta/le_audio/le_audio_types.cc
+++ b/bta/le_audio/le_audio_types.cc
@@ -31,13 +31,14 @@
 #include "client_parser.h"
 
 namespace le_audio {
+using types::LeAudioContextType;
+
 namespace set_configurations {
 using set_configurations::CodecCapabilitySetting;
 using types::acs_ac_record;
 using types::kLeAudioCodingFormatLC3;
 using types::kLeAudioDirectionSink;
 using types::kLeAudioDirectionSource;
-using types::LeAudioContextType;
 using types::LeAudioLc3Config;
 
 static uint8_t min_req_devices_cnt(
@@ -384,6 +385,61 @@
 }
 
 }  // namespace types
+
+void AppendMetadataLtvEntryForCcidList(std::vector<uint8_t>& metadata,
+                                       LeAudioContextType context_type) {
+  std::vector<uint8_t> ccid_ltv_entry;
+  /* TODO: Get CCID values from Service */
+  std::vector<uint8_t> ccid_conversational = {0x12};
+  std::vector<uint8_t> ccid_media = {0x56};
+
+  std::vector<uint8_t>* ccid_value = nullptr;
+
+  /* CCID list */
+  switch (context_type) {
+    case LeAudioContextType::CONVERSATIONAL:
+      ccid_value = &ccid_conversational;
+      break;
+    case LeAudioContextType::MEDIA:
+      ccid_value = &ccid_media;
+      break;
+    default:
+      break;
+  }
+
+  if (!ccid_value) return;
+
+  ccid_ltv_entry.push_back(static_cast<uint8_t>(types::kLeAudioMetadataTypeLen +
+                                                ccid_value->size()));
+  ccid_ltv_entry.push_back(
+      static_cast<uint8_t>(types::kLeAudioMetadataTypeCcidList));
+  ccid_ltv_entry.insert(ccid_ltv_entry.end(), ccid_value->begin(),
+                        ccid_value->end());
+
+  metadata.insert(metadata.end(), ccid_ltv_entry.begin(), ccid_ltv_entry.end());
+}
+
+void AppendMetadataLtvEntryForStreamingContext(
+    std::vector<uint8_t>& metadata, LeAudioContextType context_type) {
+  std::vector<uint8_t> streaming_context_ltv_entry;
+
+  streaming_context_ltv_entry.resize(
+      types::kLeAudioMetadataTypeLen + types::kLeAudioMetadataLenLen +
+      types::kLeAudioMetadataStreamingAudioContextLen);
+  uint8_t* streaming_context_ltv_entry_buf = streaming_context_ltv_entry.data();
+
+  UINT8_TO_STREAM(streaming_context_ltv_entry_buf,
+                  types::kLeAudioMetadataTypeLen +
+                      types::kLeAudioMetadataStreamingAudioContextLen);
+  UINT8_TO_STREAM(streaming_context_ltv_entry_buf,
+                  types::kLeAudioMetadataTypeStreamingAudioContext);
+  UINT16_TO_STREAM(streaming_context_ltv_entry_buf,
+                   static_cast<uint16_t>(context_type));
+
+  metadata.insert(metadata.end(), streaming_context_ltv_entry.begin(),
+                  streaming_context_ltv_entry.end());
+}
+
 }  // namespace le_audio
 
 std::ostream& operator<<(std::ostream& os,
diff --git a/bta/le_audio/le_audio_types.h b/bta/le_audio/le_audio_types.h
index f20aee5..b55b238 100644
--- a/bta/le_audio/le_audio_types.h
+++ b/bta/le_audio/le_audio_types.h
@@ -24,6 +24,7 @@
 
 #include <stdint.h>
 
+#include <bitset>
 #include <map>
 #include <optional>
 #include <string>
@@ -267,6 +268,12 @@
 /* Metadata types from Assigned Numbers */
 constexpr uint8_t kLeAudioMetadataTypePreferredAudioContext = 0x01;
 constexpr uint8_t kLeAudioMetadataTypeStreamingAudioContext = 0x02;
+constexpr uint8_t kLeAudioMetadataTypeCcidList = 0x03;
+
+constexpr uint8_t kLeAudioMetadataTypeLen = 1;
+constexpr uint8_t kLeAudioMetadataLenLen = 1;
+
+constexpr uint8_t kLeAudioMetadataStreamingAudioContextLen = 2;
 
 /* CSIS Types */
 constexpr uint8_t kDefaultScanDurationS = 5;
@@ -944,6 +951,11 @@
   std::vector<std::pair<uint16_t, uint32_t>> source_streams;
 };
 
+void AppendMetadataLtvEntryForCcidList(std::vector<uint8_t>& metadata,
+                                       types::LeAudioContextType context_type);
+void AppendMetadataLtvEntryForStreamingContext(
+    std::vector<uint8_t>& metadata, types::LeAudioContextType context_type);
+
 }  // namespace le_audio
 
 std::ostream& operator<<(std::ostream& os,
diff --git a/bta/le_audio/state_machine.cc b/bta/le_audio/state_machine.cc
index 6529dcd..3f0c359 100644
--- a/bta/le_audio/state_machine.cc
+++ b/bta/le_audio/state_machine.cc
@@ -152,6 +152,7 @@
         }
 
         /* All ASEs should aim to achieve target state */
+        group->SetContextType(context_type);
         SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
         PrepareAndSendCodecConfigure(group, group->GetFirstActiveDevice());
         break;
@@ -169,12 +170,21 @@
         break;
       }
 
-      case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
-        if (group->GetContextType() != context_type) {
-          /* TODO: Switch context of group */
-          group->SetContextType(context_type);
+      case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING: {
+        /* This case just updates the metadata for the stream, in case
+         * stream configuration is satisfied
+         */
+        if (!group->IsMetadataChanged(context_type)) return true;
+
+        LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
+        if (!leAudioDevice) {
+          LOG(ERROR) << __func__ << ", group has no active devices";
+          return false;
         }
-        return true;
+
+        PrepareAndSendUpdateMetadata(group, leAudioDevice, context_type);
+        break;
+      }
 
       default:
         LOG(ERROR) << "Unable to transit from " << group->GetState();
@@ -240,7 +250,10 @@
         AseStateMachineProcessEnabling(arh, ase, group, leAudioDevice);
         break;
       case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
-        AseStateMachineProcessStreaming(arh, ase, group, leAudioDevice);
+        AseStateMachineProcessStreaming(
+            arh, ase, value + le_audio::client_parser::ascs::kAseRspHdrMinLen,
+            len - le_audio::client_parser::ascs::kAseRspHdrMinLen, group,
+            leAudioDevice);
         break;
       case AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING:
         AseStateMachineProcessDisabling(arh, ase, group, leAudioDevice);
@@ -335,6 +348,13 @@
     }
   }
 
+  void FreeLinkQualityReports(LeAudioDevice* leAudioDevice) {
+    if (leAudioDevice->link_quality_timer == nullptr) return;
+
+    alarm_free(leAudioDevice->link_quality_timer);
+    leAudioDevice->link_quality_timer = nullptr;
+  }
+
   void ProcessHciNotifOnCigRemove(uint8_t status,
                                   LeAudioDeviceGroup* group) override {
     if (status) {
@@ -350,8 +370,7 @@
     if (!leAudioDevice) return;
 
     do {
-      alarm_free(leAudioDevice->link_quality_timer);
-      leAudioDevice->link_quality_timer = nullptr;
+      FreeLinkQualityReports(leAudioDevice);
 
       for (auto& ase : leAudioDevice->ases_) {
         ase.data_path_state = AudioStreamDataPathState::IDLE;
@@ -461,11 +480,7 @@
 
   void ProcessHciNotifAclDisconnected(LeAudioDeviceGroup* group,
                                       LeAudioDevice* leAudioDevice) {
-    if (leAudioDevice->link_quality_timer) {
-      alarm_free(leAudioDevice->link_quality_timer);
-      leAudioDevice->link_quality_timer = nullptr;
-    }
-
+    FreeLinkQualityReports(leAudioDevice);
     leAudioDevice->conn_id_ = GATT_INVALID_CONN_ID;
 
     if (!group) {
@@ -587,12 +602,15 @@
       ases_pair.source->data_path_state =
           AudioStreamDataPathState::CIS_ESTABLISHED;
 
-    leAudioDevice->link_quality_timer =
-        alarm_new_periodic("le_audio_cis_link_quality");
-    leAudioDevice->link_quality_timer_data = event->cis_conn_hdl;
-    alarm_set_on_mloop(leAudioDevice->link_quality_timer,
-                       linkQualityCheckInterval, link_quality_cb,
-                       &leAudioDevice->link_quality_timer_data);
+    if (osi_property_get_bool("persist.bluetooth.iso_link_quality_report",
+                              false)) {
+      leAudioDevice->link_quality_timer =
+          alarm_new_periodic("le_audio_cis_link_quality");
+      leAudioDevice->link_quality_timer_data = event->cis_conn_hdl;
+      alarm_set_on_mloop(leAudioDevice->link_quality_timer,
+                         linkQualityCheckInterval, link_quality_cb,
+                         &leAudioDevice->link_quality_timer_data);
+    }
 
     if (!leAudioDevice->HaveAllActiveAsesCisEst()) {
       /* More cis established event has to come */
@@ -637,8 +655,8 @@
       const bluetooth::hci::iso_manager::cis_disconnected_evt* event) override {
     /* Reset the disconnected CIS states */
 
-    alarm_free(leAudioDevice->link_quality_timer);
-    leAudioDevice->link_quality_timer = nullptr;
+    FreeLinkQualityReports(leAudioDevice);
+
     auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(event->cis_conn_hdl);
     if (ases_pair.sink) {
       ases_pair.sink->data_path_state = AudioStreamDataPathState::CIS_ASSIGNED;
@@ -1486,6 +1504,40 @@
                                       GATT_WRITE_NO_RSP, NULL, NULL);
   }
 
+  void PrepareAndSendUpdateMetadata(
+      LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
+      le_audio::types::LeAudioContextType context_type) {
+    std::vector<struct le_audio::client_parser::ascs::ctp_update_metadata>
+        confs;
+
+    for (; leAudioDevice;
+         leAudioDevice = group->GetNextActiveDevice(leAudioDevice)) {
+      if (!leAudioDevice->IsMetadataChanged(context_type)) continue;
+
+      auto new_metadata = leAudioDevice->GetMetadata(context_type);
+
+      /* Request server to update ASEs with new metadata */
+      for (struct ase* ase = leAudioDevice->GetFirstActiveAse(); ase != nullptr;
+           ase = leAudioDevice->GetNextActiveAse(ase)) {
+        struct le_audio::client_parser::ascs::ctp_update_metadata conf;
+
+        conf.ase_id = ase->id;
+        conf.metadata = new_metadata;
+
+        confs.push_back(conf);
+      }
+
+      std::vector<uint8_t> value;
+      le_audio::client_parser::ascs::PrepareAseCtpUpdateMetadata(confs, value);
+
+      BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
+                                        leAudioDevice->ctp_hdls_.val_hdl, value,
+                                        GATT_WRITE_NO_RSP, NULL, NULL);
+
+      return;
+    }
+  }
+
   void AseStateMachineProcessEnabling(
       struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
       LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
@@ -1524,7 +1576,8 @@
 
   void AseStateMachineProcessStreaming(
       struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
-      LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
+      uint8_t* data, uint16_t len, LeAudioDeviceGroup* group,
+      LeAudioDevice* leAudioDevice) {
     if (!group) {
       LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
 
@@ -1616,9 +1669,26 @@
 
         break;
       }
-      case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
-        /* TODO: Update metadata/Enable */
+      case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING: {
+        struct le_audio::client_parser::ascs::ase_transient_state_params rsp;
+
+        if (!ParseAseStatusTransientStateParams(rsp, len, data)) {
+          StopStream(group);
+          return;
+        }
+
+        /* Cache current set up metadata values for for further possible
+         * reconfiguration
+         */
+        for (struct ase* ase = leAudioDevice->GetFirstActiveAse();
+             ase != nullptr; ase = leAudioDevice->GetNextActiveAse(ase)) {
+          ase->metadata = rsp.metadata;
+        }
+
+        PrepareAndSendUpdateMetadata(group, leAudioDevice,
+                                     group->GetContextType());
         break;
+      }
       default:
         LOG(ERROR) << __func__ << ", invalid state transition, from: "
                    << static_cast<int>(ase->state) << ", to: "
diff --git a/bta/pan/bta_pan_api.cc b/bta/pan/bta_pan_api.cc
index dee03cf..28ad0df 100644
--- a/bta/pan/bta_pan_api.cc
+++ b/bta/pan/bta_pan_api.cc
@@ -26,7 +26,6 @@
 #include <cstdint>
 
 #include "bt_target.h"  // Must be first to define build configuration
-#if (BTA_PAN_INCLUDED == TRUE)
 
 #include "bta/pan/bta_pan_int.h"
 #include "osi/include/allocator.h"
@@ -165,23 +164,3 @@
 
   bta_sys_sendmsg(p_buf);
 }
-#else
-#include "bta/pan/bta_pan_int.h"
-#include "osi/include/osi.h"  // UNUSED_ATTR
-
-void BTA_PanEnable(UNUSED_ATTR tBTA_PAN_CBACK p_cback) {}
-
-void BTA_PanDisable(void) {}
-
-void BTA_PanSetRole(UNUSED_ATTR tBTA_PAN_ROLE role,
-                    UNUSED_ATTR tBTA_PAN_ROLE_INFO* p_user_info,
-                    UNUSED_ATTR tBTA_PAN_ROLE_INFO* p_gn_info,
-                    UNUSED_ATTR tBTA_PAN_ROLE_INFO* p_nap_info) {}
-
-void BTA_PanOpen(UNUSED_ATTR const RawAddress& bd_addr,
-                 UNUSED_ATTR tBTA_PAN_ROLE local_role,
-                 UNUSED_ATTR tBTA_PAN_ROLE peer_role) {}
-
-void BTA_PanClose(UNUSED_ATTR uint16_t handle) {}
-
-#endif /* BTA_PAN_INCLUDED */
diff --git a/bta/pan/bta_pan_ci.cc b/bta/pan/bta_pan_ci.cc
index cf8adcf..00b2187 100644
--- a/bta/pan/bta_pan_ci.cc
+++ b/bta/pan/bta_pan_ci.cc
@@ -28,8 +28,6 @@
 #include "stack/include/bt_hdr.h"
 #include "types/raw_address.h"
 
-#if (BTA_PAN_INCLUDED == TRUE)
-
 /*******************************************************************************
  *
  * Function         bta_pan_ci_tx_ready
@@ -192,39 +190,3 @@
 
   return p_buf;
 }
-
-#else
-#include "osi/include/osi.h"  // UNUSED_ATTR
-
-void bta_pan_ci_tx_ready(UNUSED_ATTR uint16_t handle) {}
-
-void bta_pan_ci_rx_ready(UNUSED_ATTR uint16_t handle) {}
-
-void bta_pan_ci_tx_flow(UNUSED_ATTR uint16_t handle, UNUSED_ATTR bool enable) {}
-
-void bta_pan_ci_rx_writebuf(UNUSED_ATTR uint16_t handle,
-                            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) {}
-
-BT_HDR* bta_pan_ci_readbuf(UNUSED_ATTR uint16_t handle,
-                           UNUSED_ATTR RawAddress& src,
-                           UNUSED_ATTR RawAddress& dst,
-                           UNUSED_ATTR uint16_t* p_protocol,
-                           UNUSED_ATTR bool* p_ext,
-                           UNUSED_ATTR bool* p_forward) {
-  return NULL;
-}
-
-void bta_pan_ci_set_pfilters(UNUSED_ATTR uint16_t handle,
-                             UNUSED_ATTR uint16_t num_filters,
-                             UNUSED_ATTR uint16_t* p_start_array,
-                             UNUSED_ATTR uint16_t* p_end_array) {}
-
-void bta_pan_ci_set_mfilters(UNUSED_ATTR uint16_t handle,
-                             UNUSED_ATTR uint16_t num_mcast_filters,
-                             UNUSED_ATTR uint8_t* p_start_array,
-                             UNUSED_ATTR uint8_t* p_end_array) {}
-
-#endif /* BTA_PAN_API */
diff --git a/bta/pan/bta_pan_main.cc b/bta/pan/bta_pan_main.cc
index cfaab26..51d9593 100644
--- a/bta/pan/bta_pan_main.cc
+++ b/bta/pan/bta_pan_main.cc
@@ -25,8 +25,6 @@
 
 #include "bt_target.h"  // Must be first to define build configuration
 
-#if (BTA_PAN_INCLUDED == TRUE)
-
 #include "bta/pan/bta_pan_int.h"
 #include "osi/include/osi.h"  // UNUSED_ATTR
 #include "stack/include/bt_hdr.h"
@@ -352,4 +350,3 @@
   }
   return freebuf;
 }
-#endif /* BTA_PAN_INCLUDED */
diff --git a/bta/test/common/mock_csis_client.h b/bta/test/common/mock_csis_client.h
index 5a6b614..33c106a 100644
--- a/bta/test/common/mock_csis_client.h
+++ b/bta/test/common/mock_csis_client.h
@@ -31,6 +31,8 @@
   MOCK_METHOD((void), LockGroup,
               (const int group_id, bool lock, bluetooth::csis::CsisLockCb cb),
               (override));
+  MOCK_METHOD((std::vector<RawAddress>), GetDeviceList, (int group_id),
+              (override));
 
   /* Called from static methods */
   MOCK_METHOD((void), Initialize,
diff --git a/bta/vc/types.h b/bta/vc/types.h
index 433b357..4461d65 100644
--- a/bta/vc/types.h
+++ b/bta/vc/types.h
@@ -20,6 +20,8 @@
 #include <queue>
 #include <vector>
 
+#include "bta/include/bta_groups.h"
+#include "osi/include/alarm.h"
 #include "raw_address.h"
 #include "types/bluetooth/uuid.h"
 
@@ -43,6 +45,47 @@
 static const Uuid kVolumeFlagsUuid                    = Uuid::From16Bit(0x2B7F);
 /* clang-format on */
 
+struct VolumeOperation {
+  int operation_id_;
+  int group_id_;
+
+  bool started_;
+
+  uint8_t opcode_;
+  std::vector<uint8_t> arguments_;
+
+  std::vector<RawAddress> devices_;
+  alarm_t* operation_timeout_;
+
+  VolumeOperation(int operation_id, int group_id, uint8_t opcode,
+                  std::vector<uint8_t> arguments,
+                  std::vector<RawAddress> devices)
+      : operation_id_(operation_id),
+        group_id_(group_id),
+        opcode_(opcode),
+        arguments_(arguments),
+        devices_(devices) {
+    auto name = "operation_timeout_" + std::to_string(operation_id);
+    operation_timeout_ = alarm_new(name.c_str());
+    started_ = false;
+  };
+
+  ~VolumeOperation() {
+    if (alarm_is_scheduled(operation_timeout_))
+      alarm_cancel(operation_timeout_);
+
+    alarm_free(operation_timeout_);
+    operation_timeout_ = nullptr;
+  }
+
+  bool IsGroupOperation(void) {
+    return (group_id_ != bluetooth::groups::kGroupUnknown);
+  }
+
+  bool IsStarted(void) { return started_; };
+  void Start(void) { started_ = true; }
+};
+
 }  // namespace internal
 }  // namespace vc
 }  // namespace bluetooth
diff --git a/bta/vc/vc.cc b/bta/vc/vc.cc
index 95d2740..dd558dc 100644
--- a/bta/vc/vc.cc
+++ b/bta/vc/vc.cc
@@ -24,16 +24,20 @@
 #include <vector>
 
 #include "bind_helpers.h"
+#include "bta/le_audio/le_audio_types.h"
+#include "bta_csis_api.h"
 #include "bta_gatt_api.h"
 #include "bta_gatt_queue.h"
 #include "bta_vc_api.h"
 #include "btif_storage.h"
 #include "devices.h"
+#include "osi/include/osi.h"
 #include "types/bluetooth/uuid.h"
 #include "types/raw_address.h"
 
 using base::Closure;
 using bluetooth::Uuid;
+using bluetooth::csis::CsisClient;
 using bluetooth::vc::ConnectionState;
 using namespace bluetooth::vc::internal;
 
@@ -64,7 +68,7 @@
   ~VolumeControlImpl() override = default;
 
   VolumeControlImpl(bluetooth::vc::VolumeControlCallbacks* callbacks)
-      : gatt_if_(0), callbacks_(callbacks) {
+      : gatt_if_(0), callbacks_(callbacks), latest_operation_id_(0) {
     BTA_GATTC_AppRegister(
         gattc_callback_static,
         base::Bind([](uint8_t client_id, uint8_t status) {
@@ -226,7 +230,8 @@
 
   void OnCharacteristicValueChanged(uint16_t conn_id, tGATT_STATUS status,
                                     uint16_t handle, uint16_t len,
-                                    uint8_t* value, void* /* data */) {
+                                    uint8_t* value, void* data,
+                                    bool is_notification) {
     VolumeControlDevice* device = volume_control_devices_.FindByConnId(conn_id);
     if (!device) {
       LOG(INFO) << __func__ << ": unknown conn_id=" << loghex(conn_id);
@@ -239,7 +244,7 @@
     }
 
     if (handle == device->volume_state_handle) {
-      OnVolumeControlStateChanged(device, len, value);
+      OnVolumeControlStateReadOrNotified(device, len, value, is_notification);
       verify_device_ready(device, handle);
       return;
     }
@@ -256,7 +261,7 @@
                            uint8_t* value) {
     LOG(INFO) << __func__ << ": handle=" << loghex(handle);
     OnCharacteristicValueChanged(conn_id, GATT_SUCCESS, handle, len, value,
-                                 nullptr);
+                                 nullptr, true);
   }
 
   void VolumeControlReadCommon(uint16_t conn_id, uint16_t handle) {
@@ -264,26 +269,130 @@
                                      nullptr);
   }
 
-  void OnVolumeControlStateChanged(VolumeControlDevice* device, uint16_t len,
-                                   uint8_t* value) {
+  void HandleAutonomusVolumeChange(VolumeControlDevice* device,
+                                   bool is_volume_change, bool is_mute_change) {
+    DLOG(INFO) << __func__ << device->address
+               << " is volume change: " << is_volume_change
+               << " is mute change: " << is_mute_change;
+
+    if (!is_volume_change && !is_mute_change) {
+      LOG(ERROR) << __func__
+                 << "Autonomous change but volume and mute did not changed.";
+      return;
+    }
+
+    auto csis_api = CsisClient::Get();
+    if (!csis_api) {
+      DLOG(INFO) << __func__ << " Csis is not available";
+      callbacks_->OnVolumeStateChanged(device->address, device->volume,
+                                       device->mute);
+      return;
+    }
+
+    auto group_id =
+        csis_api->GetGroupId(device->address, le_audio::uuid::kCapServiceUuid);
+    if (group_id == bluetooth::groups::kGroupUnknown) {
+      DLOG(INFO) << __func__ << " No group for device " << device->address;
+      callbacks_->OnVolumeStateChanged(device->address, device->volume,
+                                       device->mute);
+      return;
+    }
+
+    auto devices = csis_api->GetDeviceList(group_id);
+    for (auto it = devices.begin(); it != devices.end();) {
+      auto dev = volume_control_devices_.FindByAddress(*it);
+      if (!dev || !dev->IsConnected() || (dev->address == device->address)) {
+        it = devices.erase(it);
+      } else {
+        it++;
+      }
+    }
+
+    if (is_volume_change) {
+      std::vector<uint8_t> arg({device->volume});
+      PrepareVolumeControlOperation(devices, group_id,
+                                    kControlPointOpcodeSetAbsoluteVolume, arg);
+    }
+
+    if (is_mute_change) {
+      std::vector<uint8_t> arg;
+      uint8_t opcode =
+          device->mute ? kControlPointOpcodeMute : kControlPointOpcodeUnmute;
+      PrepareVolumeControlOperation(devices, group_id, opcode, arg);
+    }
+
+    StartQueueOperation();
+  }
+
+  void OnVolumeControlStateReadOrNotified(VolumeControlDevice* device,
+                                          uint16_t len, uint8_t* value,
+                                          bool is_notification) {
     if (len != 3) {
       LOG(INFO) << __func__ << ": malformed len=" << loghex(len);
       return;
     }
 
+    uint8_t vol;
+    uint8_t mute;
     uint8_t* pp = value;
-    STREAM_TO_UINT8(device->volume, pp);
-    STREAM_TO_UINT8(device->mute, pp);
+    STREAM_TO_UINT8(vol, pp);
+    STREAM_TO_UINT8(mute, pp);
     STREAM_TO_UINT8(device->change_counter, pp);
 
+    bool is_volume_change = (device->volume != vol);
+    device->volume = vol;
+
+    bool is_mute_change = (device->mute != mute);
+    device->mute = mute;
+
     LOG(INFO) << __func__ << " volume " << loghex(device->volume) << " mute "
               << loghex(device->mute) << " change_counter "
               << loghex(device->change_counter);
 
     if (!device->device_ready) return;
 
-    callbacks_->OnVolumeStateChanged(device->address, device->volume,
-                                     device->mute);
+    /* This is just a read, send single notification */
+    if (!is_notification) {
+      callbacks_->OnVolumeStateChanged(device->address, device->volume,
+                                       device->mute);
+      return;
+    }
+
+    auto addr = device->address;
+    auto op = find_if(ongoing_operations_.begin(), ongoing_operations_.end(),
+                      [addr](auto& operation) {
+                        auto it = find(operation.devices_.begin(),
+                                       operation.devices_.end(), addr);
+                        return it != operation.devices_.end();
+                      });
+    if (op == ongoing_operations_.end()) {
+      DLOG(INFO) << __func__ << " Could not find operation id for device: "
+                 << device->address << ". Autonomus change";
+      HandleAutonomusVolumeChange(device, is_volume_change, is_mute_change);
+      return;
+    }
+
+    DLOG(INFO) << __func__ << " operation found: " << op->operation_id_
+               << " for group id: " << op->group_id_;
+
+    /* Received notification from the device we do expect */
+    auto it = find(op->devices_.begin(), op->devices_.end(), device->address);
+    op->devices_.erase(it);
+    if (!op->devices_.empty()) {
+      DLOG(INFO) << __func__ << " wait for more responses for operation_id: "
+                 << op->operation_id_;
+      return;
+    }
+
+    if (op->IsGroupOperation())
+      callbacks_->OnGroupVolumeStateChanged(op->group_id_, device->volume,
+                                            device->mute);
+    else
+      callbacks_->OnVolumeStateChanged(device->address, device->volume,
+                                       device->mute);
+
+    ongoing_operations_.erase(op);
+    StartQueueOperation();
   }
 
   void OnVolumeControlFlagsChanged(VolumeControlDevice* device, uint16_t len,
@@ -367,8 +476,31 @@
     }
   }
 
+  void RemoveDeviceFromOperationList(const RawAddress& addr, int operation_id) {
+    auto op = find_if(ongoing_operations_.begin(), ongoing_operations_.end(),
+                      [operation_id](auto& operation) {
+                        return operation.operation_id_ == operation_id;
+                      });
+
+    if (op == ongoing_operations_.end()) {
+      LOG(ERROR) << __func__
+                 << " Could not find operation id: " << operation_id;
+      return;
+    }
+
+    auto it = find(op->devices_.begin(), op->devices_.end(), addr);
+    if (it != op->devices_.end()) {
+      op->devices_.erase(it);
+      if (op->devices_.empty()) {
+        ongoing_operations_.erase(op);
+        StartQueueOperation();
+      }
+      return;
+    }
+  }
+
   void OnWriteControlResponse(uint16_t connection_id, tGATT_STATUS status,
-                              uint16_t handle, void* /*data*/) {
+                              uint16_t handle, void* data) {
     VolumeControlDevice* device =
         volume_control_devices_.FindByConnId(connection_id);
     if (!device) {
@@ -380,28 +512,144 @@
 
     LOG(INFO) << "Write response handle: " << loghex(handle)
               << " status: " << loghex((int)(status));
+
+    if (status == GATT_SUCCESS) return;
+
+    /* In case of error, remove device from the tracking operation list */
+    RemoveDeviceFromOperationList(device->address, PTR_TO_INT(data));
+  }
+
+  static void operation_callback(void* data) {
+    instance->CancelVolumeOperation(PTR_TO_INT(data));
+  }
+
+  void StartQueueOperation(void) {
+    LOG(INFO) << __func__;
+    if (ongoing_operations_.empty()) {
+      return;
+    };
+
+    auto op = &ongoing_operations_.front();
+
+    LOG(INFO) << __func__ << " operation_id: " << op->operation_id_;
+
+    if (op->IsStarted()) {
+      LOG(INFO) << __func__ << " wait until operation " << op->operation_id_
+                << " is complete";
+      return;
+    }
+
+    op->Start();
+
+    alarm_set_on_mloop(op->operation_timeout_, 3000, operation_callback,
+                       INT_TO_PTR(op->operation_id_));
+    devices_control_point_helper(
+        op->devices_, op->opcode_,
+        op->arguments_.size() == 0 ? nullptr : &(op->arguments_));
+  }
+
+  void CancelVolumeOperation(int operation_id) {
+    LOG(INFO) << __func__ << " canceling operation_id: " << operation_id;
+
+    auto op = find_if(
+        ongoing_operations_.begin(), ongoing_operations_.end(),
+        [operation_id](auto& it) { return it.operation_id_ == operation_id; });
+
+    if (op == ongoing_operations_.end()) {
+      LOG(ERROR) << __func__
+                 << " Could not find operation_id: " << operation_id;
+      return;
+    }
+
+    /* Possibly close GATT operations */
+    ongoing_operations_.erase(op);
+    StartQueueOperation();
+  }
+
+  void ProceedVolumeOperation(int operation_id) {
+    auto op = find_if(ongoing_operations_.begin(), ongoing_operations_.end(),
+                      [operation_id](auto& operation) {
+                        return operation.operation_id_ == operation_id;
+                      });
+
+    DLOG(INFO) << __func__ << " operation_id: " << operation_id;
+
+    if (op == ongoing_operations_.end()) {
+      LOG(ERROR) << __func__
+                 << " Could not find operation_id: " << operation_id;
+      return;
+    }
+
+    DLOG(INFO) << __func__ << " procedure continued for operation_id: "
+               << op->operation_id_;
+
+    alarm_set_on_mloop(op->operation_timeout_, 3000, operation_callback,
+                       INT_TO_PTR(op->operation_id_));
+    devices_control_point_helper(op->devices_, op->opcode_, &(op->arguments_));
+  }
+
+  void PrepareVolumeControlOperation(std::vector<RawAddress>& devices,
+                                     int group_id, uint8_t opcode,
+                                     std::vector<uint8_t>& arguments) {
+    DLOG(INFO) << __func__ << " num of devices: " << devices.size()
+               << " group_id: " << group_id << " opcode: " << +opcode
+               << " arg size: " << arguments.size();
+
+    ongoing_operations_.emplace_back(latest_operation_id_++, group_id, opcode,
+                                     arguments, devices);
   }
 
   void SetVolume(std::variant<RawAddress, int> addr_or_group_id,
                  uint8_t volume) override {
-    LOG(INFO) << __func__ << " vol: " << +volume;
+    DLOG(INFO) << __func__ << " vol: " << +volume;
+
+    std::vector<uint8_t> arg({volume});
+    uint8_t opcode = kControlPointOpcodeSetAbsoluteVolume;
 
     if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
+      DLOG(INFO) << __func__ << " " << std::get<RawAddress>(addr_or_group_id);
       std::vector<RawAddress> devices = {
           std::get<RawAddress>(addr_or_group_id)};
-      std::vector<uint8_t> arg({volume});
-      devices_control_point_helper(devices,
-                                   kControlPointOpcodeSetAbsoluteVolume, &arg);
-      return;
+
+      PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown,
+                                    opcode, arg);
+    } else {
+      /* Handle group change */
+      auto group_id = std::get<int>(addr_or_group_id);
+      DLOG(INFO) << __func__ << " group: " << group_id;
+      auto csis_api = CsisClient::Get();
+      if (!csis_api) {
+        LOG(ERROR) << __func__ << " Csis is not there";
+        return;
+      }
+
+      auto devices = csis_api->GetDeviceList(group_id);
+      for (auto it = devices.begin(); it != devices.end();) {
+        auto dev = volume_control_devices_.FindByAddress(*it);
+        if (!dev || !dev->IsConnected()) {
+          it = devices.erase(it);
+        } else {
+          it++;
+        }
+      }
+
+      if (devices.empty()) {
+        LOG(ERROR) << __func__ << " group id : " << group_id
+                   << " is not connected? ";
+        return;
+      }
+
+      PrepareVolumeControlOperation(devices, group_id, opcode, arg);
     }
 
-    /* TODO implement handling group request */
+    StartQueueOperation();
   }
 
   void CleanUp() {
     LOG(INFO) << __func__;
     volume_control_devices_.Disconnect(gatt_if_);
     volume_control_devices_.Clear();
+    ongoing_operations_.clear();
     BTA_GATTC_AppDeregister(gatt_if_);
   }
 
@@ -410,6 +658,10 @@
   bluetooth::vc::VolumeControlCallbacks* callbacks_;
   VolumeControlDevices volume_control_devices_;
 
+  /* Used to track volume control operations */
+  std::list<VolumeOperation> ongoing_operations_;
+  int latest_operation_id_;
+
   void verify_device_ready(VolumeControlDevice* device, uint16_t handle) {
     if (device->device_ready) return;
 
@@ -444,7 +696,8 @@
 
   void devices_control_point_helper(std::vector<RawAddress>& devices,
                                     uint8_t opcode,
-                                    const std::vector<uint8_t>* arg) {
+                                    const std::vector<uint8_t>* arg,
+                                    int operation_id = -1) {
     volume_control_devices_.ControlPointOperation(
         devices, opcode, arg,
         [](uint16_t connection_id, tGATT_STATUS status, uint16_t handle,
@@ -453,7 +706,7 @@
             instance->OnWriteControlResponse(connection_id, status, handle,
                                              data);
         },
-        nullptr);
+        INT_TO_PTR(operation_id));
   }
 
   void gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
@@ -520,7 +773,7 @@
                                         uint8_t* value, void* data) {
     if (instance)
       instance->OnCharacteristicValueChanged(conn_id, status, handle, len,
-                                             value, data);
+                                             value, data, false);
   }
 };
 }  // namespace
diff --git a/bta/vc/vc_test.cc b/bta/vc/vc_test.cc
index 051254d..81a7353 100644
--- a/bta/vc/vc_test.cc
+++ b/bta/vc/vc_test.cc
@@ -26,10 +26,12 @@
 #include "btm_api_mock.h"
 #include "gatt/database_builder.h"
 #include "hardware/bt_gatt_types.h"
+#include "mock_csis_client.h"
 #include "types.h"
 #include "types/bluetooth/uuid.h"
 #include "types/raw_address.h"
 
+std::map<std::string, int> mock_function_count_map;
 void btif_storage_add_volume_control(const RawAddress& addr, bool auto_conn) {}
 
 namespace bluetooth {
@@ -163,6 +165,7 @@
  protected:
   void SetUp(void) override {
     bluetooth::manager::SetMockBtmInterface(&btm_interface);
+    MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_);
     gatt::SetMockBtaGattInterface(&gatt_interface);
     gatt::SetMockBtaGattQueue(&gatt_queue);
     callbacks.reset(new MockVolumeControlCallbacks());
@@ -392,6 +395,7 @@
 
   std::unique_ptr<MockVolumeControlCallbacks> callbacks;
   bluetooth::manager::MockBtmInterface btm_interface;
+  MockCsisClient mock_csis_client_module_;
   gatt::MockBtaGattInterface gatt_interface;
   gatt::MockBtaGattQueue gatt_queue;
   tBTA_GATTC_CBACK* gatt_callback;
@@ -625,6 +629,19 @@
     GetSearchCompleteEvent(conn_id);
   }
 
+  void GetNotificationEvent(uint16_t handle, std::vector<uint8_t>& value) {
+    tBTA_GATTC_NOTIFY event_data = {
+        .conn_id = conn_id,
+        .bda = test_address,
+        .handle = handle,
+        .len = (uint8_t)value.size(),
+        .is_notify = true,
+    };
+
+    std::copy(value.begin(), value.end(), event_data.value);
+    gatt_callback(BTA_GATTC_NOTIF_EVT, (tBTA_GATTC*)&event_data);
+  }
+
   void TearDown(void) override {
     TestAppUnregister();
     VolumeControlTest::TearDown();
@@ -637,6 +654,91 @@
                                               GATT_WRITE, _, _));
   VolumeControl::Get()->SetVolume(test_address, 0x10);
 }
+
+class VolumeControlCsis : public VolumeControlTest {
+ protected:
+  const RawAddress test_address_1 = GetTestAddress(0);
+  const RawAddress test_address_2 = GetTestAddress(1);
+  std::vector<RawAddress> csis_group = {test_address_1, test_address_2};
+
+  uint16_t conn_id_1 = 22;
+  uint16_t conn_id_2 = 33;
+  int group_id = 5;
+
+  void SetUp(void) override {
+    VolumeControlTest::SetUp();
+
+    ON_CALL(mock_csis_client_module_, Get())
+        .WillByDefault(Return(&mock_csis_client_module_));
+
+    // Report working CSIS
+    ON_CALL(mock_csis_client_module_, IsCsisClientRunning())
+        .WillByDefault(Return(true));
+
+    ON_CALL(mock_csis_client_module_, GetDeviceList(_))
+        .WillByDefault(Return(csis_group));
+
+    ON_CALL(mock_csis_client_module_, GetGroupId(_, _))
+        .WillByDefault(Return(group_id));
+
+    SetSampleDatabase(conn_id_1);
+    SetSampleDatabase(conn_id_2);
+
+    TestAppRegister();
+
+    TestConnect(test_address_1);
+    GetConnectedEvent(test_address_1, conn_id_1);
+    GetSearchCompleteEvent(conn_id_1);
+    TestConnect(test_address_2);
+    GetConnectedEvent(test_address_2, conn_id_2);
+    GetSearchCompleteEvent(conn_id_2);
+  }
+
+  void TearDown(void) override {
+    TestAppUnregister();
+    VolumeControlTest::TearDown();
+  }
+
+  void GetNotificationEvent(uint16_t conn_id, const RawAddress& test_address,
+                            uint16_t handle, std::vector<uint8_t>& value) {
+    tBTA_GATTC_NOTIFY event_data = {
+        .conn_id = conn_id,
+        .bda = test_address,
+        .handle = handle,
+        .len = (uint8_t)value.size(),
+        .is_notify = true,
+    };
+
+    std::copy(value.begin(), value.end(), event_data.value);
+    gatt_callback(BTA_GATTC_NOTIF_EVT, (tBTA_GATTC*)&event_data);
+  }
+};
+
+TEST_F(VolumeControlCsis, test_set_volume) {
+  /* Set value for the group */
+  EXPECT_CALL(gatt_queue,
+              WriteCharacteristic(conn_id_1, 0x0024, _, GATT_WRITE, _, _));
+  EXPECT_CALL(gatt_queue,
+              WriteCharacteristic(conn_id_2, 0x0024, _, GATT_WRITE, _, _));
+
+  VolumeControl::Get()->SetVolume(group_id, 10);
+
+  /* Now inject notification and make sure callback is sent up to Java layer */
+  EXPECT_CALL(*callbacks, OnGroupVolumeStateChanged(group_id, 0x03, true));
+
+  std::vector<uint8_t> value({0x03, 0x01, 0x02});
+  GetNotificationEvent(conn_id_1, test_address_1, 0x0021, value);
+  GetNotificationEvent(conn_id_2, test_address_2, 0x0021, value);
+}
+
+TEST_F(VolumeControlCsis, autonomus_test_set_volume) {
+  /* Now inject notification and make sure callback is sent up to Java layer */
+  EXPECT_CALL(*callbacks, OnGroupVolumeStateChanged(group_id, 0x03, false));
+
+  std::vector<uint8_t> value({0x03, 0x00, 0x02});
+  GetNotificationEvent(conn_id_1, test_address_1, 0x0021, value);
+  GetNotificationEvent(conn_id_2, test_address_2, 0x0021, value);
+}
 }  // namespace
 }  // namespace internal
 }  // namespace vc
diff --git a/btif/Android.bp b/btif/Android.bp
index c95c588..238f964 100644
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -154,7 +154,6 @@
         "src/stack_manager.cc",
     ],
     generated_headers: [
-        "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
@@ -241,6 +240,7 @@
    ],
     whole_static_libs: [
         "libbtif",
+        "libbluetooth-dumpsys",
         "libbluetooth-for-tests",
     ],
     cflags: ["-DBUILDCFG"],
@@ -261,7 +261,6 @@
     ],
     header_libs: ["libbluetooth_headers"],
     generated_headers: [
-        "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
@@ -428,9 +427,7 @@
           "test/btif_core_test.cc",
       ],
       generated_headers: [
-        "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
-        "BluetoothGeneratedDumpsysTestData_h",
         "BluetoothGeneratedPackets_h",
       ],
       header_libs: ["libbluetooth_headers"],
diff --git a/btif/src/btif_le_audio.cc b/btif/src/btif_le_audio.cc
index a1771f0..5f80fcf 100644
--- a/btif/src/btif_le_audio.cc
+++ b/btif/src/btif_le_audio.cc
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+#include <base/logging.h>
 #include <hardware/bluetooth.h>
 #include <hardware/bt_le_audio.h>
 
diff --git a/btif/src/btif_pan.cc b/btif/src/btif_pan.cc
index 038eacc..e9ed488 100644
--- a/btif/src/btif_pan.cc
+++ b/btif/src/btif_pan.cc
@@ -182,21 +182,15 @@
 }
 
 static volatile int btpan_dev_local_role;
-#if (BTA_PAN_INCLUDED == TRUE)
 static tBTA_PAN_ROLE_INFO bta_panu_info = {PANU_SERVICE_NAME, 0};
 static tBTA_PAN_ROLE_INFO bta_pan_nap_info = {PAN_NAP_SERVICE_NAME, 1};
-#endif
 
 static bt_status_t btpan_enable(int local_role) {
-#if (BTA_PAN_INCLUDED == TRUE)
   BTIF_TRACE_DEBUG("%s - local_role: %d", __func__, local_role);
   int bta_pan_role = btpan_role_to_bta(local_role);
   BTA_PanSetRole(bta_pan_role, &bta_panu_info, &bta_pan_nap_info);
   btpan_dev_local_role = local_role;
   return BT_STATUS_SUCCESS;
-#else
-  return BT_STATUS_FAIL;
-#endif
 }
 
 static int btpan_get_local_role() {
diff --git a/btif/src/stack_manager.cc b/btif/src/stack_manager.cc
index 95b4b57..45a5c61 100644
--- a/btif/src/stack_manager.cc
+++ b/btif/src/stack_manager.cc
@@ -92,6 +92,31 @@
 #error "*** Conditional Compilation Directive error"
 #endif
 
+// Once BTA_PAN_INCLUDED is no longer exposed via bt_target.h
+// this check and error statement may be removed.
+static_assert(
+    BTA_PAN_INCLUDED,
+    "#define BTA_PAN_INCLUDED preprocessor compilation flag is unsupported"
+    "  Pan profile is always included in the bluetooth stack"
+    "*** Conditional Compilation Directive error");
+
+// Once PAN_SUPPORTS_ROLE_NAP is no longer exposed via bt_target.h
+// this check and error statement may be removed.
+static_assert(
+    PAN_SUPPORTS_ROLE_NAP,
+    "#define PAN_SUPPORTS_ROLE_NAP preprocessor compilation flag is unsupported"
+    "  Pan profile always supports network access point in the bluetooth stack"
+    "*** Conditional Compilation Directive error");
+
+// Once PAN_SUPPORTS_ROLE_PANU is no longer exposed via bt_target.h
+// this check and error statement may be removed.
+static_assert(
+    PAN_SUPPORTS_ROLE_PANU,
+    "#define PAN_SUPPORTS_ROLE_PANU preprocessor compilation flag is "
+    "unsupported"
+    "  Pan profile always supports user as a client in the bluetooth stack"
+    "*** Conditional Compilation Directive error");
+
 void main_thread_shut_down();
 void main_thread_start_up();
 void BTA_dm_on_hw_on();
@@ -165,7 +190,7 @@
 extern const module_t btif_config_module;
 extern const module_t btsnoop_module;
 extern const module_t bt_utils_module;
-extern const module_t controller_module;
+extern const module_t gd_controller_module;
 extern const module_t gd_idle_module;
 extern const module_t gd_shim_module;
 extern const module_t hci_module;
@@ -183,6 +208,7 @@
     {BTIF_CONFIG_MODULE, &btif_config_module},
     {BTSNOOP_MODULE, &btsnoop_module},
     {BT_UTILS_MODULE, &bt_utils_module},
+    {GD_CONTROLLER_MODULE, &gd_controller_module},
     {GD_IDLE_MODULE, &gd_idle_module},
     {GD_SHIM_MODULE, &gd_shim_module},
     {INTEROP_MODULE, &interop_module},
@@ -200,7 +226,7 @@
     }
   }
 
-  abort();
+  LOG_ALWAYS_FATAL("Cannot find module %s, aborting", name);
   return nullptr;
 }
 #else
diff --git a/build/dpkg/floss/README.mkdn b/build/dpkg/floss/README.mkdn
new file mode 100644
index 0000000..bc3c423
--- /dev/null
+++ b/build/dpkg/floss/README.mkdn
@@ -0,0 +1,16 @@
+Debian 10
+
+build-dpkg:
+    - Builds a binary debian package
+
+package:
+    - Debian package
+
+`./build-dpkg` will verify, download, build, and install everything needed to build the floss dpkg.
+
+How to use:
+
+Run `sudo install-dependencies` first then run `build-dpkg`
+
+TODO:
+ - Figure out versioning for DEBIAN/control
diff --git a/build/dpkg/floss/build-dpkg b/build/dpkg/floss/build-dpkg
new file mode 100755
index 0000000..e917e9c
--- /dev/null
+++ b/build/dpkg/floss/build-dpkg
@@ -0,0 +1,183 @@
+#!/bin/bash
+
+DRY_RUN=""
+if [ $# -gt 0 ]; then
+    if [ "$1" == "--dry-run" ]; then
+        DRY_RUN="echo "
+    fi
+fi
+
+REQUIRED="git cargo"
+
+for name in $(echo ${REQUIRED});
+do
+    type -P "$name" &>/dev/null || { echo "Install '$name'" >&2; exit 1;}
+done
+
+FIRST_DIR="$(pwd)"
+
+# Vars
+URL_GN="http://commondatastorage.googleapis.com/chromeos-localmirror/distfiles/gn-3e43fac03281e2f5e5ae5f27c8e9a6bb45966ea9.bin"
+URL_PLATFORM2_GIT="https://chromium.googlesource.com/chromiumos/platform2"
+URL_RUST_CRATES_GIT="https://chromium.googlesource.com/chromiumos/third_party/rust_crates"
+URL_PROTO_LOGGING_GIT="https://android.googlesource.com/platform/frameworks/proto_logging"
+CHROMIUM_BRANCH="release-R92-13982.B"
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+PARENT_DIR="$(echo ${SCRIPT_DIR} | rev | cut -d '/' -f 2- | rev )"
+TMP_DIR=$(mktemp -d)
+
+trap ctrl_c INT
+
+function ctrl_c() {
+    rm -rf "${TMP_DIR}"
+    exit 1
+}
+
+echo Generating source package in "${TMP_DIR}"
+OUT_DIR="${TMP_DIR}/out"
+BIN_DIR="${TMP_DIR}/bin"
+
+${DRY_RUN} mkdir -p "${OUT_DIR}"
+${DRY_RUN} mkdir -p "${BIN_DIR}"
+
+pushd "${BIN_DIR}"
+wget -O gn "${URL_GN}"
+popd
+export PATH="${PATH}:${BIN_DIR}"
+
+# Check dependencies
+# libchrome requires modp_b64
+APT_REQUIRED="modp-b64 libchrome flatbuffers-compiler flex g++-multilib gcc-multilib generate-ninja gnupg gperf libc++-dev libdbus-1-dev libevent-dev libevent-dev libflatbuffers-dev libflatbuffers1 libgl1-mesa-dev libglib2.0-dev liblz4-tool libncurses5 libnss3-dev libprotobuf-dev libre2-9 libssl-dev libtinyxml2-dev libx11-dev libxml2-utils ninja-build openssl protobuf-compiler unzip x11proto-core-dev xsltproc zip zlib1g-dev"
+
+# SPEED UP TEST, REMOVE ME
+APT_REQUIRED="modp-b64 libchrome flatbuffers-compiler"
+
+APT_MISSING=()
+for name in $(echo ${APT_REQUIRED});
+do
+    R="$(apt -qq list "${name}" 2>/dev/null | grep "installed")"
+    if [ "${R}" == "" ]; then
+        echo "Need to install '${name}'" >&2;
+        if [ "${name}" == "modp-b64" ]; then
+            echo "${name} source is available to build in this repository"
+            echo Run the following to build and install:
+            echo "  pushd ${PARENT_DIR}/${name}/"
+            echo "  ./gen-src-pkg.sh ${OUT_DIR}"
+            echo "  sudo dpkg -i ${OUT_DIR}"/${name}*.deb || ctrl_c
+            echo "  popd"
+            ${DRY_RUN} rm -rf ${TMP_DIR}
+            exit 1
+        elif [ "${name}" == "libchrome" ]; then
+            echo "${name} source is available to build in this repository"
+            echo Run the following to build and install:
+            echo   pushd "${PARENT_DIR}/${name}/"
+            echo   ./gen-src-pkg.sh "${OUT_DIR}"
+            echo   sudo dpkg -i "${OUT_DIR}"/${name}*.deb || ctrl_c
+            echo   popd
+            ${DRY_RUN} rm -rf ${TMP_DIR}
+            exit 1
+        else
+            APT_MISSING+=("${name}")
+        fi
+    fi
+done
+
+APT_MISSING_LEN="${#APT_MISSING[@]}"
+
+if [ $APT_MISSING_LEN -gt 0 ]; then
+    echo "Missing Packages:"
+    echo "    ${APT_MISSING[*]}"
+    echo
+    echo Run the following to build and install:
+    echo "  sudo apt install" "${APT_MISSING[*]}" || ctrl_c
+    ${DRY_RUN} rm -rf ${TMP_DIR}
+    exit 1
+fi
+
+# Check cargo for cxxbridge-cmd
+HAS_CXX="$(cargo install --list | grep cxxbridge-cmd)"
+if [ "$HAS_CXX" == "" ]; then
+    echo "Missing cxxbridge-cmd cargo package"
+    echo Run the following to build and install:
+    echo   cargo install cxxbridge-cmd || ctrl_c
+    ${DRY_RUN} rm -rf ${TMP_DIR}
+    exit 1
+fi
+
+HAS_CXX="$(cargo install --list | grep cargo-proc-macro)"
+if [ "$HAS_CXX" == "" ]; then
+    echo "Missing cargo-proc-macro cargo package"
+    echo Run the following to build and install:
+    echo   cargo install cargo-proc-macro || ctrl_c
+    ${DRY_RUN} rm -rf ${TMP_DIR}
+    exit 1
+fi
+
+# Git
+GIT_DIR="${OUT_DIR}/repos"
+GIT_DIR_PLATFORM2="${GIT_DIR}/platform2"
+GIT_DIR_PLATFORM2_COMMON_MK="${GIT_DIR_PLATFORM2}/common-mk"
+GIT_DIR_PLATFORM2_GN="${GIT_DIR_PLATFORM2}/.gn"
+GIT_DIR_RUST_CRATES="${GIT_DIR}/rust_crates"
+GIT_DIR_PROTO_LOGGING="${GIT_DIR}/proto_logging"
+GIT_DIR_BT="$(echo "${PARENT_DIR}" | rev | cut -d '/' -f 3- | rev)"
+
+# Staging
+STAGING_DIR="${OUT_DIR}/staging"
+STAGING_DIR_PLATFORM2="${STAGING_DIR}/platform2"
+STAGING_DIR_COMMON_MK="${STAGING_DIR}/common-mk"
+STAGING_DIR_GN="${STAGING_DIR}/.gn"
+STAGING_DIR_BT="${STAGING_DIR}/bt"
+# No it isn't a typo, use 'rust'
+STAGING_DIR_RUST_CRATES="${STAGING_DIR}/rust"
+STAGING_DIR_PROTO_LOGGING="${STAGING_DIR}/proto_logging"
+
+OUTPUT_DIR="${OUT_DIR}/output"
+EXTERNAL_DIR="${STAGING_DIR}/external"
+EXTERNAL_DIR_RUST="${EXTERNAL_DIR}/rust"
+EXTERNAL_DIR_PROTO_LOGGING="${EXTERNAL_DIR}/proto_logging"
+
+${DRY_RUN} mkdir -p "${GIT_DIR}"
+${DRY_RUN} mkdir -p "${STAGING_DIR}"
+${DRY_RUN} mkdir -p "${OUTPUT_DIR}"
+${DRY_RUN} mkdir -p "${EXTERNAL_DIR}"
+
+${DRY_RUN} git clone -b "${CHROMIUM_BRANCH}" "${URL_PLATFORM2_GIT}" "${GIT_DIR_PLATFORM2}"
+
+${DRY_RUN} git clone "${URL_RUST_CRATES_GIT}" "${GIT_DIR_RUST_CRATES}"
+${DRY_RUN} git clone "${URL_PROTO_LOGGING_GIT}" "${GIT_DIR_PROTO_LOGGING}"
+
+${DRY_RUN} ln -s "${GIT_DIR_PLATFORM2_COMMON_MK}" "${STAGING_DIR_COMMON_MK}" || ctrl_c
+${DRY_RUN} ln -s "${GIT_DIR_PLATFORM2_GN}" "${STAGING_DIR_GN}" || ctrl_c
+${DRY_RUN} ln -s "${GIT_DIR_BT}" "${STAGING_DIR_BT}" || ctrl_c
+${DRY_RUN} ln -s "${GIT_DIR_RUST_CRATES}" "${EXTERNAL_DIR_RUST}" || ctrl_c
+${DRY_RUN} ln -s "${GIT_DIR_PROTO_LOGGING}" "${EXTERNAL_DIR_PROTO_LOGGING}" || ctrl_c
+
+${DRY_RUN} "${GIT_DIR_BT}"/build.py --bootstrap-dir "$(readlink -f "${OUT_DIR}")" --libdir /usr/lib || ctrl_c
+
+PKG_DIR="${SCRIPT_DIR}/package"
+PKG_USR_DIR="${PKG_DIR}/usr"
+
+OUT_PKG_DIR="${OUT_DIR}/package"
+OUT_PKG_USR_DIR="${OUT_PKG_DIR}/usr"
+
+BIN_OUTPUT="${OUTPUT_DIR}/debug"
+
+BTCLIENT_BIN="${BIN_OUTPUT}/btclient"
+BTMANAGERD_BIN="${BIN_OUTPUT}/btmanagerd"
+BTADAPTERD_BIN="${BIN_OUTPUT}/btadapterd"
+
+${DRY_RUN} cp -r "${PKG_DIR}" "${OUT_DIR}/"
+
+${DRY_RUN} cp "${BTCLIENT_BIN}" "${OUT_PKG_USR_DIR}/bin/"
+${DRY_RUN} cp "${BTMANAGERD_BIN}" "${OUT_PKG_USR_DIR}/libexec/bluetooth/"
+${DRY_RUN} cp "${BTADAPTERD_BIN}" "${OUT_PKG_USR_DIR}/libexec/bluetooth/"
+
+${DRY_RUN} dpkg-deb --build "${OUT_PKG_DIR}" "${FIRST_DIR}/floss.deb"
+
+${DRY_RUN} rm -rf ${TMP_DIR}
+
+echo
+echo "Now run:"
+echo "    sudo dpkg -i "${FIRST_DIR}"/floss.deb"
diff --git a/build/dpkg/floss/install-dependencies b/build/dpkg/floss/install-dependencies
new file mode 100755
index 0000000..be2fd20
--- /dev/null
+++ b/build/dpkg/floss/install-dependencies
@@ -0,0 +1,95 @@
+#!/bin/bash
+
+DRY_RUN=""
+if [ $# -gt 0 ]; then
+    if [ "$1" == "--dry-run" ]; then
+        DRY_RUN="echo "
+    fi
+fi
+
+echo "Checking for dependencies..."
+
+REQUIRED="git cargo"
+
+for name in $(echo ${REQUIRED});
+do
+    type -P "$name" &>/dev/null || { echo "Install '$name'" >&2; exit 1;}
+done
+
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+PARENT_DIR="$(echo ${SCRIPT_DIR} | rev | cut -d '/' -f 2- | rev )"
+
+TMP_DIR=$(mktemp -d)
+OUT_DIR="${TMP_DIR}/out"
+
+trap ctrl_c INT
+
+function ctrl_c() {
+    rm -rf "${TMP_DIR}"
+    exit 1
+}
+
+# Check dependencies
+# libchrome requires modp_b64
+APT_REQUIRED="flatbuffers-compiler flex g++-multilib gcc-multilib generate-ninja \
+gnupg gperf libc++-dev libdbus-1-dev libevent-dev libevent-dev libflatbuffers-dev libflatbuffers1 \
+libgl1-mesa-dev libglib2.0-dev liblz4-tool libncurses5 libnss3-dev libprotobuf-dev libre2-9 \
+libssl-dev libtinyxml2-dev libx11-dev libxml2-utils ninja-build openssl protobuf-compiler unzip \
+x11proto-core-dev xsltproc zip zlib1g-dev debmake ninja-build modp-b64 libchrome"
+
+APT_MISSING=()
+for name in $(echo ${APT_REQUIRED});
+do
+    R="$(apt -qq list "${name}" 2>/dev/null | grep "installed")"
+    if [ "${R}" == "" ]; then
+        echo "Need to install '${name}'" >&2;
+        if [ "${name}" == "modp-b64" ]; then
+            echo "${name} source is available to build in this repository"
+            # dir name is different than package name :'(
+            ${DRY_RUN} pushd "${PARENT_DIR}/modp_b64/"
+            ${DRY_RUN} ./gen-src-pkg.sh "${OUT_DIR}"
+            ${DRY_RUN} sudo dpkg -i "${OUT_DIR}"/${name}*.deb || ctrl_c
+            ${DRY_RUN} popd
+        elif [ "${name}" == "libchrome" ]; then
+            echo "${name} source is available to build in this repository"
+            ${DRY_RUN} pushd "${PARENT_DIR}/${name}/"
+            ${DRY_RUN} ./gen-src-pkg.sh "${OUT_DIR}"
+            ${DRY_RUN} sudo dpkg -i "${OUT_DIR}"/${name}*.deb || ctrl_c
+            ${DRY_RUN} popd
+        else
+            APT_MISSING+=("${name}")
+        fi
+    fi
+done
+
+APT_MISSING_LEN="${#APT_MISSING[@]}"
+
+if [ $APT_MISSING_LEN -gt 0 ]; then
+    echo "Missing Packages:"
+    echo "    ${APT_MISSING[*]}"
+    ${DRY_RUN} sudo apt install "${APT_MISSING[*]}" || ctrl_c
+else
+    rm -rf "${TMP_DIR}"
+    exit 0
+fi
+
+echo Generating missing packages in "${TMP_DIR}"
+
+# Check cargo for cxxbridge-cmd
+HAS_CXX="$(cargo install --list | grep cxxbridge-cmd)"
+if [ "$HAS_CXX" == "" ]; then
+    echo "Missing cxxbridge-cmd cargo package"
+    echo "Installing 'cxxbridge-cmd'" >&2
+    ${DRY_RUN} cargo install cxxbridge-cmd || ctrl_c
+fi
+
+HAS_CXX="$(cargo install --list | grep cargo-proc-macro)"
+if [ "$HAS_CXX" == "" ]; then
+    echo "Missing cargo-proc-macro cargo package"
+    echo "Installing 'cxxbridge-cmd'" >&2
+    ${DRY_RUN} cargo install cargo-proc-macro || ctrl_c
+fi
+
+rm -rf "${TMP_DIR}"
+echo "DONE"
diff --git a/build/dpkg/floss/package/DEBIAN/control b/build/dpkg/floss/package/DEBIAN/control
new file mode 100644
index 0000000..3bb5d35
--- /dev/null
+++ b/build/dpkg/floss/package/DEBIAN/control
@@ -0,0 +1,11 @@
+Package: floss
+Section: custom
+Priority: optional
+Maintainer: Martin Brabham <optedoblivion@google.com>
+Version: 0.1
+Homepage: https://www.google.com
+Depends: debmake, ninja-build, flatbuffers-compiler, flex, g++-multilib, gcc-multilib, generate-ninja, gnupg, gperf, libc++-dev, libdbus-1-dev, libevent-dev, libevent-dev, libflatbuffers-dev, libflatbuffers1, libgl1-mesa-dev, libglib2.0-dev, liblz4-tool, libncurses5, libnss3-dev, libprotobuf-dev, libre2-9, libssl-dev, libtinyxml2-dev, libx11-dev, libxml2-utils, ninja-build, openssl, protobuf-compiler, unzip, x11proto-core-dev, xsltproc, zip, zlib1g-dev, modp-b64, libchrome
+Architecture: all
+Essential: no
+Installed-Size: 490MB
+Description: The Fluoride Bluetooth stack on Linux
diff --git a/build/dpkg/floss/package/etc/dbus-1/system.d/org.chromium.bluetooth.conf b/build/dpkg/floss/package/etc/dbus-1/system.d/org.chromium.bluetooth.conf
new file mode 100644
index 0000000..4e226e7
--- /dev/null
+++ b/build/dpkg/floss/package/etc/dbus-1/system.d/org.chromium.bluetooth.conf
@@ -0,0 +1,37 @@
+<!DOCTYPE busconfig PUBLIC
+          "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+          "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+  <!-- Only root or user bluetooth can own the btmanagerd service -->
+  <policy user="bluetooth">
+    <allow own="org.chromium.bluetooth"/>
+    <allow own="org.chromium.bluetooth.Manager"/>
+    <allow own="org.chromium.bluetooth.ManagerCallback"/>
+  </policy>
+  <policy user="root">
+    <allow own="org.chromium.bluetooth"/>
+    <allow own="org.chromium.bluetooth.Manager"/>
+    <allow own="org.chromium.bluetooth.ManagerCallback"/>
+  </policy>
+
+  <!-- Allow anyone to invoke methods on btmanagerd server,  -->
+  <!-- Will likely change this as the project matures  -->
+  <policy context="default">
+    <allow send_destination="org.chromium.bluetooth"/>
+    <allow send_destination="org.chromium.bluetooth.Manager"/>
+    <allow send_destination="org.chromium.bluetooth.ManagerCallback"/>
+  </policy>
+
+  <!-- Allow access to everything to the group "bluetooth" -->
+  <policy group="bluetooth">
+    <allow send_destination="org.chromium.bluetooth"/>
+    <allow send_destination="org.chromium.bluetooth.Manager"/>
+    <allow send_destination="org.chromium.bluetooth.ManagerCallback"/>
+  </policy>
+  <policy user="root">
+    <allow send_destination="org.chromium.bluetooth"/>
+    <allow send_destination="org.chromium.bluetooth.Manager"/>
+    <allow send_destination="org.chromium.bluetooth.ManagerCallback"/>
+  </policy>
+</busconfig>
diff --git a/build/dpkg/floss/package/lib/systemd/system/btadapterd@.service b/build/dpkg/floss/package/lib/systemd/system/btadapterd@.service
new file mode 100644
index 0000000..e63c56d
--- /dev/null
+++ b/build/dpkg/floss/package/lib/systemd/system/btadapterd@.service
@@ -0,0 +1,22 @@
+[Unit]
+Description=Floss Bluetooth Adapter service
+Documentation=man:btadapterd(8)
+ConditionPathIsDirectory=/sys/class/bluetooth
+After=bluetooth.target btmanagerd.service
+
+[Service]
+Type=dbus
+BusName=org.chromium.bluetooth
+ExecStart=/usr/libexec/bluetooth/btadapterd --hci=%i
+ExecStartPost=/usr/bin/rm -f /var/run/bluetooth/bluetooth%i.pid
+TimeoutStopSec=3
+TimeoutStartSec=5
+NotifyAccess=main
+CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
+LimitNPROC=1
+ProtectHome=true
+ProtectSystem=full
+
+[Install]
+WantedBy=bluetooth.target btmanagerd.service
+Alias=dbus-org.btadapterd.service
diff --git a/build/dpkg/floss/package/lib/systemd/system/btmanagerd.service b/build/dpkg/floss/package/lib/systemd/system/btmanagerd.service
new file mode 100644
index 0000000..43bba3b
--- /dev/null
+++ b/build/dpkg/floss/package/lib/systemd/system/btmanagerd.service
@@ -0,0 +1,21 @@
+[Unit]
+Description=Floss Bluetooth service
+Documentation=man:btmanagerd(8)
+ConditionPathIsDirectory=/sys/class/bluetooth
+After=bluetooth.target
+
+[Service]
+Type=dbus
+BusName=org.chromium.bluetooth.Manager
+ExecStart=/usr/libexec/bluetooth/btmanagerd --systemd
+NotifyAccess=main
+CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
+LimitNPROC=1
+ProtectHome=true
+ProtectSystem=full
+TimeoutStopSec=3
+TimeoutStartSec=5
+
+[Install]
+WantedBy=bluetooth.target
+Alias=dbus-org.btmanagerd.service
diff --git a/embdrv/lc3/Common/DctIV.cpp b/embdrv/lc3/Common/DctIV.cpp
index 0fd4684..798322e 100644
--- a/embdrv/lc3/Common/DctIV.cpp
+++ b/embdrv/lc3/Common/DctIV.cpp
@@ -116,6 +116,8 @@
 };
 
 #elif defined USE_OWN_FFT
+#include <complex>
+
 #include "fft.h"
 #endif
 
@@ -136,7 +138,17 @@
   dctIVconfig = new KissfftConfig(NF / 2);
 
 #elif defined USE_OWN_FFT
-// setup is not needed
+
+  int N = NF / 2;
+  std::complex<double>* twiddle = new std::complex<double>[N];
+  dctIVconfig = twiddle;
+  const double pi = std::acos(-1);
+  for (uint16_t n = 0; n < N; n++) {
+    twiddle[n] =
+        std::complex<double>(std::cos(-pi * (8 * n + 1) / (8.0 * N * 2)),
+                             std::sin(-pi * (8 * n + 1) / (8.0 * N * 2)));
+  }
+
 #endif
 
   for (uint16_t n = 0; n < NF; n++) {
@@ -159,7 +171,9 @@
   }
 
 #elif defined USE_OWN_FFT
-// cleanup is not needed
+  std::complex<double>* twiddle = (std::complex<double>*)dctIVconfig;
+  delete[] twiddle;
+
 #endif
   if (nullptr != in) {
     delete[] in;
@@ -250,6 +264,14 @@
     in[NF - n] = buffer;
   }
 
+  std::complex<double>* twiddle = (std::complex<double>*)dctIVconfig;
+  for (uint16_t n = 0; n < NF / 2; n++) {
+    double real = in[2 * n + 0];
+    double imag = in[2 * n + 1];
+    in[2 * n + 0] = real * twiddle[n].real() - imag * twiddle[n].imag();
+    in[2 * n + 1] = real * twiddle[n].imag() + imag * twiddle[n].real();
+  }
+
   for (uint16_t n = 0; n < NF / 2; n++) {
     inbuf[n].re = in[2 * n];
     inbuf[n].im = in[2 * n + 1];
@@ -258,8 +280,17 @@
   fft_complex* actal_output = fft(false, inbuf, NF / 2, inbuf, outbuf);
 
   for (uint16_t n = 0; n < NF / 2; n++) {
-    out[2 * n] = actal_output[n].re;
-    out[NF - 2 * n - 1] = actal_output[n].im;
+    double real = actal_output[n].re;
+    double imag = actal_output[n].im;
+    out[2 * n + 0] = 2 * (real * twiddle[n].real() - imag * twiddle[n].imag());
+    out[2 * n + 1] = 2 * (real * twiddle[n].imag() + imag * twiddle[n].real());
+  }
+
+  for (uint16_t n = 1; n < NF / 2; n += 2) {
+    double buffer;
+    buffer = out[n];
+    out[n] = -out[NF - n];
+    out[NF - n] = -buffer;
   }
 
 #else
diff --git a/gd/Android.bp b/gd/Android.bp
index 744f6a4..fadab26 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -178,7 +178,6 @@
     generated_headers: [
         "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
-        "BluetoothGeneratedDumpsysBundledSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
     shared_libs: [
@@ -189,6 +188,7 @@
         "libgrpc_wrap",
     ],
     static_libs: [
+        "libbluetooth-dumpsys",
         "libbluetooth-protos",
         "libbluetooth_rust_interop",
         "libbt-platform-protos-lite",
@@ -250,6 +250,7 @@
     ],
     static_libs: [
         "breakpad_client",
+        "libbluetooth-dumpsys",
         "libbluetooth-protos",
         "libbluetooth_gd",
         "libflatbuffers-cpp",
@@ -347,13 +348,14 @@
     ],
     generated_headers: [
         "BluetoothGeneratedBundlerSchema_h_bfbs",
-        "BluetoothGeneratedDumpsysBundledSchema_h",
-        "BluetoothGeneratedDumpsysBundledTestSchema_h",
         "BluetoothGeneratedDumpsysDataSchema_h",
         "BluetoothGeneratedDumpsysTestData_h",
         "BluetoothGeneratedPackets_h",
     ],
     static_libs: [
+        "libbluetooth-dumpsys",
+        "libbluetooth-dumpsys-test",
+        "libbluetooth-dumpsys-unittest",
         "libbluetooth-protos",
         "libbluetooth_gd",
         "libc++fs",
diff --git a/gd/BUILD.gn b/gd/BUILD.gn
index 67310e3..d12a380 100644
--- a/gd/BUILD.gn
+++ b/gd/BUILD.gn
@@ -48,7 +48,7 @@
   deps = [
     "//bt/gd:BluetoothGeneratedDumpsysDataSchema_h",
     "//bt/gd:BluetoothGeneratedPackets_h",
-    "//bt/gd/dumpsys:BluetoothGeneratedDumpsysBundledSchema_h",
+    "//bt/gd/dumpsys:libbluetooth-dumpsys",
     "//bt/gd/rust/shim:init_flags_bridge_header",
   ]
 }
diff --git a/gd/att/att_module.cc b/gd/att/att_module.cc
index 7f41133..99846fb 100644
--- a/gd/att/att_module.cc
+++ b/gd/att/att_module.cc
@@ -67,7 +67,7 @@
   l2cap::classic::L2capClassicModule* l2cap_classic_module_;
 };
 
-void AttModule::ListDependencies(ModuleList* list) {
+void AttModule::ListDependencies(ModuleList* list) const {
   list->add<l2cap::le::L2capLeModule>();
   list->add<l2cap::classic::L2capClassicModule>();
 }
@@ -91,4 +91,4 @@
 // }
 
 }  // namespace att
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/att/att_module.h b/gd/att/att_module.h
index 3501d64..a04993c 100644
--- a/gd/att/att_module.h
+++ b/gd/att/att_module.h
@@ -30,7 +30,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/btaa/activity_attribution.h b/gd/btaa/activity_attribution.h
index 9bba347..f21f365 100644
--- a/gd/btaa/activity_attribution.h
+++ b/gd/btaa/activity_attribution.h
@@ -62,7 +62,7 @@
 
  protected:
   std::string ToString() const override;
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   DumpsysDataFinisher GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const override;  // Module
diff --git a/gd/btaa/android/activity_attribution.cc b/gd/btaa/android/activity_attribution.cc
index 3b72feb..34d4a36 100644
--- a/gd/btaa/android/activity_attribution.cc
+++ b/gd/btaa/android/activity_attribution.cc
@@ -183,7 +183,7 @@
   return "Btaa Module";
 }
 
-void ActivityAttribution::ListDependencies(ModuleList* list) {}
+void ActivityAttribution::ListDependencies(ModuleList* list) const {}
 
 void ActivityAttribution::Start() {
   pimpl_ = std::make_unique<impl>(this);
diff --git a/gd/btaa/host/activity_attribution.cc b/gd/btaa/host/activity_attribution.cc
index a2f7d70..5bd58b9 100644
--- a/gd/btaa/host/activity_attribution.cc
+++ b/gd/btaa/host/activity_attribution.cc
@@ -35,7 +35,7 @@
   return "Btaa Module";
 }
 
-void ActivityAttribution::ListDependencies(ModuleList* list) {}
+void ActivityAttribution::ListDependencies(ModuleList* list) const {}
 
 void ActivityAttribution::Start() {}
 
diff --git a/gd/btaa/linux/activity_attribution.cc b/gd/btaa/linux/activity_attribution.cc
index dd6a7db..391ad5d 100644
--- a/gd/btaa/linux/activity_attribution.cc
+++ b/gd/btaa/linux/activity_attribution.cc
@@ -38,7 +38,7 @@
   return "Btaa Module";
 }
 
-void ActivityAttribution::ListDependencies(ModuleList* list) {}
+void ActivityAttribution::ListDependencies(ModuleList* list) const {}
 
 void ActivityAttribution::Start() {}
 
diff --git a/gd/cert/py_le_acl_manager.py b/gd/cert/py_le_acl_manager.py
index c52a5ab..18ba9d3 100644
--- a/gd/cert/py_le_acl_manager.py
+++ b/gd/cert/py_le_acl_manager.py
@@ -23,6 +23,7 @@
 from cert.closable import safeClose
 from bluetooth_packets_python3 import hci_packets
 from cert.truth import assertThat
+from datetime import timedelta
 from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
 
 
@@ -98,6 +99,14 @@
         self.listen_for_incoming_connections()
         return self.complete_incoming_connection()
 
+    def wait_for_connection_fail(self, token):
+        assertThat(self.outgoing_connection_event_streams[token]).isNotNone()
+        event_stream = self.outgoing_connection_event_streams[token][0]
+        connection_fail = HciCaptures.LeConnectionCompleteCapture()
+        assertThat(event_stream).emits(connection_fail, timeout=timedelta(seconds=35))
+        complete = connection_fail.get()
+        assertThat(complete.GetStatus() == hci_packets.ErrorCode.CONNECTION_ACCEPT_TIMEOUT).isTrue()
+
     def cancel_connection(self, token):
         assertThat(token in self.outgoing_connection_event_streams).isTrue()
         pair = self.outgoing_connection_event_streams.pop(token)
@@ -112,6 +121,14 @@
         self.next_token += 1
         return token
 
+    def initiate_background_and_direct_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.CreateBackgroundAndDirectConnection(remote_addr)), 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)
diff --git a/gd/common/strings.h b/gd/common/strings.h
index 9ae2a4b..3c0d314 100644
--- a/gd/common/strings.h
+++ b/gd/common/strings.h
@@ -49,7 +49,7 @@
 }
 
 template <>
-inline std::string ToHexString<signed long>(signed long x) {
+inline std::string ToHexString<>(signed long x) {
   if (x < 0) {
     if (x == LONG_MIN) return "LONG_MIN";
     return "-" + ToHexString<signed long>(-x);
@@ -60,6 +60,14 @@
   return tmp.str();
 }
 
+template <>
+inline std::string ToHexString<>(unsigned int x) {
+  std::stringstream tmp;
+  tmp << "0x" << std::internal << std::hex << std::setfill('0') << std::setw(sizeof(unsigned int) * 2)
+      << (unsigned long)x;
+  return tmp.str();
+}
+
 // Convert value into a hex decimal formatted string in lower case, prefixed with 0s
 template <class InputIt>
 std::string ToHexString(InputIt first, InputIt last) {
@@ -98,6 +106,7 @@
 std::optional<bool> BoolFromString(const std::string& str);
 std::string ToString(bool value);
 
+// Migrate this method to std::format when C++20 becomes available
 // printf like formatting to std::string
 // format must contains format information, to print a string use StringFormat("%s", str)
 template <typename... Args>
@@ -105,8 +114,8 @@
   auto size = std::snprintf(nullptr, 0, format.c_str(), args...);
   ASSERT_LOG(size >= 0, "return value %d, error %d, text '%s'", size, errno, strerror(errno));
   // Add 1 for terminating null byte
-  char buffer[size + 1];
-  auto actual_size = std::snprintf(buffer, sizeof(buffer), format.c_str(), args...);
+  std::vector<char> buffer(size + 1);
+  auto actual_size = std::snprintf(buffer.data(), buffer.size(), format.c_str(), args...);
   ASSERT_LOG(
       size == actual_size,
       "asked size %d, actual size %d, error %d, text '%s'",
@@ -115,7 +124,7 @@
       errno,
       strerror(errno));
   // Exclude the terminating null byte
-  return std::string(buffer, size);
+  return std::string(buffer.data(), size);
 }
 
 inline std::string StringFormatTime(const std::string& format, const struct std::tm& tm) {
diff --git a/gd/common/strings_test.cc b/gd/common/strings_test.cc
index 8a9ff1a..7da1ccd 100644
--- a/gd/common/strings_test.cc
+++ b/gd/common/strings_test.cc
@@ -78,6 +78,16 @@
   ASSERT_EQ(ToHexString('a'), "0x61");
 }
 
+TEST(StringsTest, to_hex_string_from_number_unsigned_int) {
+  ASSERT_EQ(ToHexString(0U), "0x00000000");
+  ASSERT_EQ(ToHexString(1U), "0x00000001");
+  ASSERT_EQ(ToHexString(3U), "0x00000003");
+  ASSERT_EQ(ToHexString(25U), "0x00000019");
+  ASSERT_EQ(ToHexString(UINT_MAX), "0xffffffff");
+  ASSERT_EQ(ToHexString(1U + UINT_MAX), "0x00000000");  // Rolled over
+  ASSERT_EQ(ToHexString(2U + UINT_MAX), "0x00000001");  // Rolled over
+}
+
 TEST(StringsTest, trim_string_test) {
   ASSERT_EQ(StringTrim("  aa bb"), "aa bb");
   ASSERT_EQ(StringTrim("aa bb "), "aa bb");
diff --git a/gd/dumpsys/Android.bp b/gd/dumpsys/Android.bp
index 74b32dd..0e4ca99 100644
--- a/gd/dumpsys/Android.bp
+++ b/gd/dumpsys/Android.bp
@@ -28,16 +28,17 @@
 
 genrule {
     name: "BluetoothGeneratedDumpsysTestData_h",
+    visibility: ["//visibility:public"],
     tools: [
         "flatc",
     ],
     cmd: "$(location flatc) -I system/bt/gd -b --schema -o $(genDir) --cpp $(in) ",
     srcs: [
-            "test_data/root.fbs",
-            "test_data/bar.fbs",
-            "test_data/baz.fbs",
-            "test_data/foo.fbs",
-            "test_data/qux.fbs",
+        "test_data/root.fbs",
+        "test_data/bar.fbs",
+        "test_data/baz.fbs",
+        "test_data/foo.fbs",
+        "test_data/qux.fbs",
     ],
     out: [
         "root_generated.h",
@@ -50,16 +51,17 @@
 
 genrule {
     name: "BluetoothGeneratedDumpsysTestData_bfbs",
+    visibility: ["//visibility:private"],
     tools: [
         "flatc",
     ],
     cmd: "$(location flatc) -I system/bt/gd -b --schema -o $(genDir) --cpp $(in) ",
     srcs: [
-            "test_data/root.fbs",
-            "test_data/bar.fbs",
-            "test_data/baz.fbs",
-            "test_data/foo.fbs",
-            "test_data/qux.fbs",
+        "test_data/root.fbs",
+        "test_data/bar.fbs",
+        "test_data/baz.fbs",
+        "test_data/foo.fbs",
+        "test_data/qux.fbs",
     ],
     out: [
         "root.bfbs",
@@ -71,7 +73,8 @@
 }
 
 genrule {
-    name: "BluetoothGeneratedDumpsysTestSchema_h",
+    name: "BluetoothGeneratedDumpsysTestSchema_cc",
+    visibility: ["//visibility:private"],
     tools: [
         "bluetooth_flatbuffer_bundler",
     ],
@@ -80,12 +83,13 @@
         ":BluetoothGeneratedDumpsysBinarySchema_bfbs",
     ],
     out: [
-         "dumpsys_module_schema_data.h",
+         "dumpsys_module_schema_data.cc",
     ],
 }
 
 genrule {
-    name: "BluetoothGeneratedDumpsysBundledSchema_h",
+    name: "BluetoothGeneratedDumpsysBundledSchema_cc",
+    visibility: ["//visibility:private"],
     tools: [
             "bluetooth_flatbuffer_bundler",
     ],
@@ -94,12 +98,13 @@
         ":BluetoothGeneratedDumpsysBinarySchema_bfbs",
     ],
     out: [
-         "generated_dumpsys_bundled_schema.h",
+         "generated_dumpsys_bundled_schema.cc",
     ],
 }
 
 genrule {
-    name: "BluetoothGeneratedDumpsysBundledTestSchema_h",
+    name: "BluetoothGeneratedDumpsysBundledTestSchema_cc",
+    visibility: ["//visibility:private"],
     tools: [
             "bluetooth_flatbuffer_bundler",
     ],
@@ -108,7 +113,7 @@
         ":BluetoothGeneratedDumpsysTestData_bfbs",
     ],
     out: [
-         "generated_dumpsys_bundled_test_schema.h",
+         "generated_dumpsys_bundled_test_schema.cc",
     ],
 }
 
@@ -126,6 +131,45 @@
     ],
 }
 
+cc_library {
+    name: "libbluetooth-dumpsys",
+    host_supported: true,
+    defaults: [
+        "gd_defaults",
+        "gd_clang_file_coverage",
+        "gd_clang_tidy",
+    ],
+    generated_sources: [
+        "BluetoothGeneratedDumpsysBundledSchema_cc",
+    ],
+}
+
+cc_library {
+    name: "libbluetooth-dumpsys-test",
+    host_supported: true,
+    defaults: [
+        "gd_defaults",
+        "gd_clang_file_coverage",
+        "gd_clang_tidy",
+    ],
+    generated_sources: [
+        "BluetoothGeneratedDumpsysBundledTestSchema_cc",
+   ],
+}
+
+cc_library {
+    name: "libbluetooth-dumpsys-unittest",
+    host_supported: true,
+    defaults: [
+        "gd_defaults",
+        "gd_clang_file_coverage",
+        "gd_clang_tidy",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedDumpsysTestSchema_cc",
+   ],
+}
+
 cc_test {
     name: "bluetooth_flatbuffer_tests",
     test_suites: ["device-tests"],
diff --git a/gd/dumpsys/BUILD.gn b/gd/dumpsys/BUILD.gn
index 82779f8..a280ec8 100644
--- a/gd/dumpsys/BUILD.gn
+++ b/gd/dumpsys/BUILD.gn
@@ -29,9 +29,10 @@
   deps = [ "//bt/gd:gd_default_deps" ]
 }
 
-bt_flatc_bundler("BluetoothGeneratedDumpsysBundledSchema_h") {
+bt_flatc_bundler("libbluetooth-dumpsys") {
   root_name = "bluetooth.DumpsysData"
   filename = "generated_dumpsys_bundled_schema"
   namespace = "bluetooth::dumpsys"
   deps = [ "//bt/gd:BluetoothGeneratedDumpsysBinarySchema_bfbs" ]
+  configs = [ "//bt/gd:gd_defaults" ]
 }
diff --git a/gd/dumpsys/bundler/Android.bp b/gd/dumpsys/bundler/Android.bp
index 67081c9..adef9d6 100644
--- a/gd/dumpsys/bundler/Android.bp
+++ b/gd/dumpsys/bundler/Android.bp
@@ -9,6 +9,7 @@
 
 filegroup {
     name: "BluetoothFlatbufferBundlerSources",
+    visibility: ["//visibility:private"],
     srcs: [
         "bundler.cc",
         "main.cc",
@@ -17,23 +18,29 @@
 
 filegroup {
     name: "BluetoothFlatbufferBundlerTestSources",
+    visibility: ["//visibility:private"],
     srcs: [
         "bundler.cc",
         "test.cc",
     ],
 }
 
+// Flatbuffer bundler schema that wraps the bundled binary module schema
 genrule {
     name: "BluetoothGeneratedBundlerSchema_h_bfbs",
+    visibility: [
+        "//system/bt/gd",
+        "//system/bt/main",
+    ],
     tools: [
         "flatc",
     ],
     cmd: "$(location flatc) -I system/bt/gd -b --schema -o $(genDir) --cpp $(in) ",
     srcs: [
-        "bundler.fbs",
+        "bundler_schema.fbs",
     ],
     out: [
-        "bundler_generated.h", "bundler.bfbs",
+        "bundler_schema_generated.h", "bundler_schema.bfbs",
     ],
 }
 
diff --git a/gd/dumpsys/bundler/BUILD.gn b/gd/dumpsys/bundler/BUILD.gn
index ada6b9c..9d149dc 100644
--- a/gd/dumpsys/bundler/BUILD.gn
+++ b/gd/dumpsys/bundler/BUILD.gn
@@ -17,7 +17,7 @@
 import("//common-mk/flatbuffer.gni")
 
 bt_flatc_binary_schema("BluetoothGeneratedBundlerSchema_h_bfbs") {
-  sources = [ "bundler.fbs" ]
+  sources = [ "bundler_schema.fbs" ]
   include_dir = "bt/gd"
   gen_header = true
 }
diff --git a/gd/dumpsys/bundler/bundler.cc b/gd/dumpsys/bundler/bundler.cc
index d37aca0..08a2cca 100644
--- a/gd/dumpsys/bundler/bundler.cc
+++ b/gd/dumpsys/bundler/bundler.cc
@@ -13,17 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "bundler.h"
+
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+
 #include <cassert>
 #include <list>
 #include <map>
 #include <vector>
 
-#include "bundler.h"
-#include "bundler_generated.h"
+#include "bundler_schema_generated.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
 
@@ -145,24 +147,11 @@
   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 %sdata_[] = {\n",
-      namespace_prefix.c_str());
+  fprintf(fp, "extern const std::string& GetBundledSchemaData();\n");
+  fprintf(fp, "const unsigned char %sdata_[%zu] = {\n", namespace_prefix.c_str(), data_len);
 
   for (auto i = 0; i < data_len; i++) {
     fprintf(fp, " 0x%02x", data[i]);
@@ -181,14 +170,11 @@
       namespace_prefix.c_str(),
       namespace_prefix.c_str(),
       namespace_prefix.c_str());
-  fprintf(fp, "}  // namespace\n");
-  fprintf(fp, "const unsigned char* %s::data = %sdata_;\n", opts.ns_name, namespace_prefix.c_str());
-  fprintf(fp, "const size_t %s::data_size = %zu;\n", opts.ns_name, data_len);
-  fprintf(
-      fp,
-      "const std::string& %s::GetBundledSchemaData() { return %sstring_data_; }\n",
-      opts.ns_name,
-      namespace_prefix.c_str());
+  fprintf(fp, "const std::string& GetBundledSchemaData() { return %sstring_data_; }\n", namespace_prefix.c_str());
+
+  for_each(namespaces.crbegin(), namespaces.crend(), [fp](const std::string& s) {
+    fprintf(fp, "}  // namespace %s\n", s.c_str());
+  });
 }
 
 int ReadBundledSchema() {
@@ -264,7 +250,7 @@
   }
 
   std::string header(opts.gen);
-  header += ("/" + std::string(opts.filename) + ".h");
+  header += ("/" + std::string(opts.filename) + ".cc");
   FILE* fp = fopen(header.c_str(), "w+");
   if (fp == nullptr) {
     fprintf(stdout, "Unable to open for writing header file:%s\n", header.c_str());
diff --git a/gd/dumpsys/bundler/bundler.gni b/gd/dumpsys/bundler/bundler.gni
index d58697a..20207d5 100644
--- a/gd/dumpsys/bundler/bundler.gni
+++ b/gd/dumpsys/bundler/bundler.gni
@@ -144,7 +144,7 @@
     ]
 
     outputs = [
-      "${target_gen_dir}/${invoker.filename}.h",
+      "${target_gen_dir}/${invoker.filename}.cc",
       "${target_gen_dir}/${invoker.filename}",
     ]
 
@@ -158,20 +158,12 @@
     include_dirs = [ "${target_gen_dir}" ]
   }
 
-  generated_file(target_name) {
-    outputs = [ "${target_gen_dir}/${target_name}.files" ]
-    output_conversion = "list lines"
-    data_keys = [ "all_outputs" ]
+  source_set(target_name) {
+    sources = [
+      "${target_gen_dir}/${invoker.filename}.cc",
+    ]
 
-    all_dependent_configs = [ ":${all_dependent_config_name}" ]
-    if (defined(invoker.all_dependent_configs)) {
-      all_dependent_configs += invoker.all_dependent_configs
-    }
-
-    deps = [ ":${action_name}" ]
-    if (defined(invoker.deps)) {
-      deps += invoker.deps
-    }
+    public_deps = [ ":$action_name" ]
 
     if (defined(invoker.configs)) {
       configs += invoker.configs
diff --git a/gd/dumpsys/bundler/bundler.fbs b/gd/dumpsys/bundler/bundler_schema.fbs
similarity index 100%
rename from gd/dumpsys/bundler/bundler.fbs
rename to gd/dumpsys/bundler/bundler_schema.fbs
diff --git a/gd/dumpsys/bundler/main.cc b/gd/dumpsys/bundler/main.cc
index 23eb1e0..e040ef2 100644
--- a/gd/dumpsys/bundler/main.cc
+++ b/gd/dumpsys/bundler/main.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 #include "bundler.h"
-#include "bundler_generated.h"
+#include "bundler_schema_generated.h"
 
 int main(int argc, char** argv) {
   ParseArgs(argc, argv);
diff --git a/gd/dumpsys/bundler/test.cc b/gd/dumpsys/bundler/test.cc
index 87648c3..929b6ff 100644
--- a/gd/dumpsys/bundler/test.cc
+++ b/gd/dumpsys/bundler/test.cc
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 #include <gtest/gtest.h>
+
 #include <list>
 #include <vector>
 
 #include "bundler.h"
-#include "bundler_generated.h"
+#include "bundler_schema_generated.h"
 #include "flatbuffers/flatbuffers.h"
 
 // Must be run from the same directory as the test data 'test.bfbs'.
diff --git a/gd/dumpsys/dumpsys.h b/gd/dumpsys/dumpsys.h
new file mode 100644
index 0000000..6d2cafd
--- /dev/null
+++ b/gd/dumpsys/dumpsys.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <string>
+
+namespace bluetooth {
+namespace dumpsys {
+const std::string& GetBundledSchemaData();
+}  // namespace dumpsys
+}  // namespace bluetooth
diff --git a/gd/dumpsys/dumpsys_test_data.h b/gd/dumpsys/dumpsys_test_data.h
new file mode 100644
index 0000000..763d702
--- /dev/null
+++ b/gd/dumpsys/dumpsys_test_data.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <string>
+
+namespace testing {
+const std::string& GetBundledSchemaData();
+}  // namespace testing
diff --git a/gd/dumpsys/filter_test.cc b/gd/dumpsys/filter_test.cc
index e789ef0..89a98f8 100644
--- a/gd/dumpsys/filter_test.cc
+++ b/gd/dumpsys/filter_test.cc
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-#include <list>
-#include <queue>
-
 #include "dumpsys/filter.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <list>
+#include <queue>
+
+#include "dumpsys/dumpsys_test_data.h"
 #include "test_data/bar.h"
 #include "test_data/baz.h"
 #include "test_data/foo.h"
@@ -29,12 +30,6 @@
 #include "test_data/root.h"
 
 namespace testing {
-extern const unsigned char* data;
-extern const size_t data_size;
-const std::string& GetBundledSchemaData();
-}  // namespace testing
-
-namespace testing {
 
 using namespace bluetooth;
 
diff --git a/gd/dumpsys/reflection_schema.cc b/gd/dumpsys/reflection_schema.cc
index 809fb0a..52ceaa2 100644
--- a/gd/dumpsys/reflection_schema.cc
+++ b/gd/dumpsys/reflection_schema.cc
@@ -15,8 +15,10 @@
  */
 
 #include "dumpsys/reflection_schema.h"
+
 #include <string>
-#include "bundler_generated.h"
+
+#include "bundler_schema_generated.h"
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "os/log.h"
diff --git a/gd/dumpsys/reflection_schema.h b/gd/dumpsys/reflection_schema.h
index b2dc679..c449644 100644
--- a/gd/dumpsys/reflection_schema.h
+++ b/gd/dumpsys/reflection_schema.h
@@ -17,7 +17,8 @@
 #pragma once
 
 #include <string>
-#include "bundler_generated.h"
+
+#include "bundler_schema_generated.h"
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 
diff --git a/gd/dumpsys/reflection_schema_test.cc b/gd/dumpsys/reflection_schema_test.cc
index 0e9bf80..99ec49c 100644
--- a/gd/dumpsys/reflection_schema_test.cc
+++ b/gd/dumpsys/reflection_schema_test.cc
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
+#include "dumpsys/reflection_schema.h"
+
 #include <gtest/gtest.h>
 
-#include "dumpsys/reflection_schema.h"
-#include "generated_dumpsys_bundled_test_schema.h"
+#include "dumpsys/dumpsys_test_data.h"
 
-// TODO(cmanton) fix bundler to split header/code
-// #include "generated_dumpsys_bundled_schema.h"
 namespace bluetooth {
 namespace dumpsys {
 extern const unsigned char* data;
diff --git a/gd/facade/read_only_property_server.cc b/gd/facade/read_only_property_server.cc
index 5a3c42a..182e5e7 100644
--- a/gd/facade/read_only_property_server.cc
+++ b/gd/facade/read_only_property_server.cc
@@ -37,7 +37,7 @@
   hci::Controller* controller_;
 };
 
-void ReadOnlyPropertyServerModule::ListDependencies(ModuleList* list) {
+void ReadOnlyPropertyServerModule::ListDependencies(ModuleList* list) const {
   GrpcFacadeModule::ListDependencies(list);
   list->add<hci::Controller>();
 }
diff --git a/gd/facade/read_only_property_server.h b/gd/facade/read_only_property_server.h
index ac068b4..55faec1 100644
--- a/gd/facade/read_only_property_server.h
+++ b/gd/facade/read_only_property_server.h
@@ -32,7 +32,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/grpc/grpc_module.cc b/gd/grpc/grpc_module.cc
index bf4f362..3dd40ab 100644
--- a/gd/grpc/grpc_module.cc
+++ b/gd/grpc/grpc_module.cc
@@ -24,7 +24,7 @@
 namespace bluetooth {
 namespace grpc {
 
-void GrpcModule::ListDependencies(ModuleList* list) {}
+void GrpcModule::ListDependencies(ModuleList* list) const {}
 
 void GrpcModule::Start() {
   ASSERT(!started_);
@@ -105,7 +105,7 @@
 
 const ::bluetooth::ModuleFactory GrpcModule::Factory = ::bluetooth::ModuleFactory([]() { return new GrpcModule(); });
 
-void GrpcFacadeModule::ListDependencies(ModuleList* list) {
+void GrpcFacadeModule::ListDependencies(ModuleList* list) const {
   list->add<GrpcModule>();
 }
 
diff --git a/gd/grpc/grpc_module.h b/gd/grpc/grpc_module.h
index 77bf1b2..3e47df7 100644
--- a/gd/grpc/grpc_module.h
+++ b/gd/grpc/grpc_module.h
@@ -43,7 +43,7 @@
   void RunGrpcLoop();
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
@@ -62,7 +62,7 @@
   friend GrpcModule;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/hal/facade.cc b/gd/hal/facade.cc
index 931b2d7..d33a834 100644
--- a/gd/hal/facade.cc
+++ b/gd/hal/facade.cc
@@ -139,7 +139,7 @@
   ::bluetooth::grpc::GrpcEventQueue<::bluetooth::facade::Data> pending_iso_events_{"StreamIso"};
 };
 
-void HciHalFacadeModule::ListDependencies(ModuleList* list) {
+void HciHalFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<HciHal>();
 }
diff --git a/gd/hal/facade.h b/gd/hal/facade.h
index ed846d5..7b4b1ad 100644
--- a/gd/hal/facade.h
+++ b/gd/hal/facade.h
@@ -33,7 +33,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
   void Stop() override;
diff --git a/gd/hal/fuzz/fuzz_hci_hal.h b/gd/hal/fuzz/fuzz_hci_hal.h
index 6024f3a..5a73784 100644
--- a/gd/hal/fuzz/fuzz_hci_hal.h
+++ b/gd/hal/fuzz/fuzz_hci_hal.h
@@ -43,7 +43,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const override {}
   void Start() override {}
   void Stop() override {}
 
diff --git a/gd/hal/hci_hal_android_hidl.cc b/gd/hal/hci_hal_android_hidl.cc
index 4979ef0..ee90943 100644
--- a/gd/hal/hci_hal_android_hidl.cc
+++ b/gd/hal/hci_hal_android_hidl.cc
@@ -192,7 +192,7 @@
   }
 
  protected:
-  void ListDependencies(ModuleList* list) override {
+  void ListDependencies(ModuleList* list) const {
     list->add<SnoopLogger>();
     if (common::init_flags::btaa_hci_is_enabled()) {
       list->add<activity_attribution::ActivityAttribution>();
diff --git a/gd/hal/hci_hal_host.cc b/gd/hal/hci_hal_host.cc
index b41fbd4..b751d28 100644
--- a/gd/hal/hci_hal_host.cc
+++ b/gd/hal/hci_hal_host.cc
@@ -261,7 +261,7 @@
   }
 
  protected:
-  void ListDependencies(ModuleList* list) override {
+  void ListDependencies(ModuleList* list) const {
     list->add<SnoopLogger>();
   }
 
diff --git a/gd/hal/hci_hal_host_rootcanal.cc b/gd/hal/hci_hal_host_rootcanal.cc
index a579b41..5789f3f 100644
--- a/gd/hal/hci_hal_host_rootcanal.cc
+++ b/gd/hal/hci_hal_host_rootcanal.cc
@@ -156,7 +156,7 @@
   }
 
  protected:
-  void ListDependencies(ModuleList* list) override {
+  void ListDependencies(ModuleList* list) const {
     list->add<SnoopLogger>();
   }
 
diff --git a/gd/hal/snoop_logger.cc b/gd/hal/snoop_logger.cc
index 0b6ad64..f517c8e 100644
--- a/gd/hal/snoop_logger.cc
+++ b/gd/hal/snoop_logger.cc
@@ -351,7 +351,7 @@
   }
 }
 
-void SnoopLogger::ListDependencies(ModuleList* list) {
+void SnoopLogger::ListDependencies(ModuleList* list) const {
   // We have no dependencies
 }
 
diff --git a/gd/hal/snoop_logger.h b/gd/hal/snoop_logger.h
index a332e3d..22e88d8 100644
--- a/gd/hal/snoop_logger.h
+++ b/gd/hal/snoop_logger.h
@@ -83,7 +83,7 @@
   void Capture(const HciPacket& packet, Direction direction, PacketType type);
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   DumpsysDataFinisher GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const override;
diff --git a/gd/hci/acl_manager.cc b/gd/hci/acl_manager.cc
index bb6b1b0..eefb83b 100644
--- a/gd/hci/acl_manager.cc
+++ b/gd/hci/acl_manager.cc
@@ -160,6 +160,9 @@
 }
 
 void AclManager::CreateLeConnection(AddressWithType address_with_type, bool is_direct) {
+  if (!is_direct) {
+    CallOn(pimpl_->le_impl_, &le_impl::add_device_to_background_connection_list, address_with_type);
+  }
   CallOn(pimpl_->le_impl_, &le_impl::create_le_connection, address_with_type, true, is_direct);
 }
 
@@ -209,6 +212,7 @@
 }
 
 void AclManager::CancelLeConnect(AddressWithType address_with_type) {
+  CallOn(pimpl_->le_impl_, &le_impl::remove_device_from_background_connection_list, address_with_type);
   CallOn(pimpl_->le_impl_, &le_impl::cancel_connect, address_with_type);
 }
 
@@ -279,7 +283,7 @@
   CallOn(pimpl_->round_robin_scheduler_, &RoundRobinScheduler::SetLinkPriority, handle, high_priority);
 }
 
-void AclManager::ListDependencies(ModuleList* list) {
+void AclManager::ListDependencies(ModuleList* list) const {
   list->add<HciLayer>();
   list->add<Controller>();
   list->add<storage::StorageModule>();
diff --git a/gd/hci/acl_manager.h b/gd/hci/acl_manager.h
index bef5ae8..effa6db 100644
--- a/gd/hci/acl_manager.h
+++ b/gd/hci/acl_manager.h
@@ -124,7 +124,7 @@
  static const ModuleFactory Factory;
 
 protected:
- void ListDependencies(ModuleList* list) override;
+ void ListDependencies(ModuleList* list) const override;
 
  void Start() override;
 
diff --git a/gd/hci/acl_manager/le_impl.h b/gd/hci/acl_manager/le_impl.h
index 9b4ec14..356db1c 100644
--- a/gd/hci/acl_manager/le_impl.h
+++ b/gd/hci/acl_manager/le_impl.h
@@ -159,6 +159,10 @@
     if (status == ErrorCode::UNKNOWN_CONNECTION && pause_connection) {
       // connection canceled by LeAddressManager.OnPause(), will auto reconnect by LeAddressManager.OnResume()
       return;
+    } else if (status == ErrorCode::UNKNOWN_CONNECTION && remote_address.GetAddress() == Address::kEmpty) {
+      // direct connect canceled due to connection timeout, start background connect
+      create_le_connection(remote_address, false, false);
+      return;
     } else {
       canceled_connections_.erase(remote_address);
       ready_to_unregister = true;
@@ -217,6 +221,10 @@
     if (status == ErrorCode::UNKNOWN_CONNECTION && pause_connection) {
       // connection canceled by LeAddressManager.OnPause(), will auto reconnect by LeAddressManager.OnResume()
       return;
+    } else if (status == ErrorCode::UNKNOWN_CONNECTION && remote_address.GetAddress() == Address::kEmpty) {
+      // direct connect canceled due to connection timeout, start background connect
+      create_le_connection(remote_address, false, false);
+      return;
     } else {
       canceled_connections_.erase(remote_address);
       ready_to_unregister = true;
@@ -547,7 +555,14 @@
     if (create_connection_timeout_alarms_.find(address_with_type) != create_connection_timeout_alarms_.end()) {
       create_connection_timeout_alarms_.at(address_with_type).Cancel();
       create_connection_timeout_alarms_.erase(address_with_type);
-      cancel_connect(address_with_type);
+      if (background_connections_.find(address_with_type) != background_connections_.end()) {
+        direct_connections_.erase(address_with_type);
+        le_acl_connection_interface_->EnqueueCommand(
+            LeCreateConnectionCancelBuilder::Create(),
+            handler_->BindOnce(&le_impl::on_create_connection_cancel_complete, common::Unretained(this)));
+      } else {
+        cancel_connect(address_with_type);
+      }
       le_client_handler_->Post(common::BindOnce(
           &LeConnectionCallbacks::OnLeConnectFail,
           common::Unretained(le_client_callbacks_),
@@ -667,6 +682,14 @@
     return true;
   }
 
+  void add_device_to_background_connection_list(AddressWithType address_with_type) {
+    background_connections_.insert(address_with_type);
+  }
+
+  void remove_device_from_background_connection_list(AddressWithType address_with_type) {
+    background_connections_.erase(address_with_type);
+  }
+
   void OnPause() override {
     pause_connection = true;
     if (connecting_le_.empty()) {
@@ -743,6 +766,8 @@
   std::set<AddressWithType> connecting_le_;
   std::set<AddressWithType> canceled_connections_;
   std::set<AddressWithType> direct_connections_;
+  // Set of devices that will not be removed from connect list after direct connect timeout
+  std::set<AddressWithType> background_connections_;
   bool address_manager_registered = false;
   bool ready_to_unregister = false;
   bool pause_connection = false;
diff --git a/gd/hci/acl_manager_test.cc b/gd/hci/acl_manager_test.cc
index 3975e27..554c0d9 100644
--- a/gd/hci/acl_manager_test.cc
+++ b/gd/hci/acl_manager_test.cc
@@ -115,7 +115,7 @@
  protected:
   void Start() override {}
   void Stop() override {}
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const {}
 };
 
 class TestHciLayer : public HciLayer {
@@ -284,7 +284,7 @@
     return acl_queue_.GetUpEnd();
   }
 
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const {}
   void Start() override {
     RegisterEventHandler(EventCode::COMMAND_COMPLETE,
                          GetHandler()->BindOn(this, &TestHciLayer::CommandCompleteCallback));
diff --git a/gd/hci/cert/le_acl_manager_test_lib.py b/gd/hci/cert/le_acl_manager_test_lib.py
index 2abff9f..067bb71 100644
--- a/gd/hci/cert/le_acl_manager_test_lib.py
+++ b/gd/hci/cert/le_acl_manager_test_lib.py
@@ -285,3 +285,66 @@
                               hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'!'))
 
         assertThat(self.dut_le_acl).emits(lambda packet: b'Hello!' in packet.payload)
+
+    def test_background_connection(self):
+        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+        self.register_for_le_event(hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE)
+        self.set_privacy_policy_static()
+
+        # Start background and direct connection
+        token = self.dut_le_acl_manager.initiate_background_and_direct_connection(
+            remote_addr=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes('0C:05:04:03:02:01', 'utf8')),
+                type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)))
+
+        # Wait for direct connection timeout
+        self.dut_le_acl_manager.wait_for_connection_fail(token)
+
+        # Cert Advertises
+        advertising_handle = 0
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                advertising_handle,
+                hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                400,
+                450,
+                7,
+                hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                '00:00:00:00:00:00',
+                hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                0xF8,
+                1,  #SID
+                hci_packets.Enable.DISABLED  # Scan request notification
+            ))
+
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0C:05:04:03:02:01'))
+
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_A_Cert'))
+
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
+
+        gap_short_name = hci_packets.GapData()
+        gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
+        gap_short_name.data = list(bytes(b'Im_A_C'))
+
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
+                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name]))
+
+        enabled_set = hci_packets.EnabledSet()
+        enabled_set.advertising_handle = advertising_handle
+        enabled_set.duration = 0
+        enabled_set.max_extended_advertising_events = 0
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
+
+        # Check background connection complete
+        self.dut_le_acl_manager.complete_outgoing_connection(token)
\ No newline at end of file
diff --git a/gd/hci/controller.cc b/gd/hci/controller.cc
index d4f1cac..0fa8fed 100644
--- a/gd/hci/controller.cc
+++ b/gd/hci/controller.cc
@@ -1142,7 +1142,7 @@
 
 const ModuleFactory Controller::Factory = ModuleFactory([]() { return new Controller(); });
 
-void Controller::ListDependencies(ModuleList* list) {
+void Controller::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
 }
 
diff --git a/gd/hci/controller.h b/gd/hci/controller.h
index 520d9ff..c706723 100644
--- a/gd/hci/controller.h
+++ b/gd/hci/controller.h
@@ -183,7 +183,7 @@
   static constexpr uint64_t kDefaultLeEventMask = 0x0000000041021e7f;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/hci/controller_test.cc b/gd/hci/controller_test.cc
index 75f6db1..3b1df9d 100644
--- a/gd/hci/controller_test.cc
+++ b/gd/hci/controller_test.cc
@@ -249,7 +249,7 @@
     return command;
   }
 
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const {}
   void Start() override {}
   void Stop() override {}
 
diff --git a/gd/hci/facade/acl_manager_facade.cc b/gd/hci/facade/acl_manager_facade.cc
index 384426e..4923d9f 100644
--- a/gd/hci/facade/acl_manager_facade.cc
+++ b/gd/hci/facade/acl_manager_facade.cc
@@ -557,7 +557,7 @@
   uint32_t current_connection_request_{0};
 };
 
-void AclManagerFacadeModule::ListDependencies(ModuleList* list) {
+void AclManagerFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<AclManager>();
 }
diff --git a/gd/hci/facade/acl_manager_facade.h b/gd/hci/facade/acl_manager_facade.h
index 9537349..6e153de 100644
--- a/gd/hci/facade/acl_manager_facade.h
+++ b/gd/hci/facade/acl_manager_facade.h
@@ -31,7 +31,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/hci/facade/controller_facade.cc b/gd/hci/facade/controller_facade.cc
index 45cbca4..48b66e2 100644
--- a/gd/hci/facade/controller_facade.cc
+++ b/gd/hci/facade/controller_facade.cc
@@ -151,7 +151,7 @@
   Controller* controller_;
 };
 
-void ControllerFacadeModule::ListDependencies(ModuleList* list) {
+void ControllerFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<Controller>();
 }
diff --git a/gd/hci/facade/controller_facade.h b/gd/hci/facade/controller_facade.h
index c1f0f18..7037f38 100644
--- a/gd/hci/facade/controller_facade.h
+++ b/gd/hci/facade/controller_facade.h
@@ -31,7 +31,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/hci/facade/facade.cc b/gd/hci/facade/facade.cc
index fb1e232..8bd459d 100644
--- a/gd/hci/facade/facade.cc
+++ b/gd/hci/facade/facade.cc
@@ -223,7 +223,7 @@
   bool completed_packets_callback_registered_{false};
 };
 
-void HciFacadeModule::ListDependencies(ModuleList* list) {
+void HciFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<HciLayer>();
   list->add<Controller>();
diff --git a/gd/hci/facade/facade.h b/gd/hci/facade/facade.h
index 0966aa5..77ac362 100644
--- a/gd/hci/facade/facade.h
+++ b/gd/hci/facade/facade.h
@@ -31,7 +31,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/hci/facade/le_acl_manager_facade.cc b/gd/hci/facade/le_acl_manager_facade.cc
index ffc0dc1..75c6aac 100644
--- a/gd/hci/facade/le_acl_manager_facade.cc
+++ b/gd/hci/facade/le_acl_manager_facade.cc
@@ -75,6 +75,25 @@
     return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
   }
 
+  ::grpc::Status CreateBackgroundAndDirectConnection(
+      ::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()));
+    // Create background connection first
+    acl_manager_->CreateLeConnection(peer, /* is_direct */ false);
+    acl_manager_->CreateLeConnection(peer, /* is_direct */ true);
+    wait_for_background_connection_complete = true;
+    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,
@@ -243,6 +262,7 @@
       success.set_payload(builder_to_string(std::move(builder)));
       per_connection_events_[current_connection_request_]->OnIncomingEvent(success);
     }
+    wait_for_background_connection_complete = false;
     current_connection_request_++;
   }
 
@@ -252,7 +272,9 @@
     LeConnectionEvent fail;
     fail.set_payload(builder_to_string(std::move(builder)));
     per_connection_events_[current_connection_request_]->OnIncomingEvent(fail);
-    current_connection_request_++;
+    if (!wait_for_background_connection_complete) {
+      current_connection_request_++;
+    }
   }
 
   class Connection : public LeConnectionManagementCallbacks {
@@ -310,9 +332,10 @@
   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};
+  bool wait_for_background_connection_complete = false;
 };
 
-void LeAclManagerFacadeModule::ListDependencies(ModuleList* list) {
+void LeAclManagerFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<AclManager>();
 }
diff --git a/gd/hci/facade/le_acl_manager_facade.h b/gd/hci/facade/le_acl_manager_facade.h
index 899a6cc..45d35fe 100644
--- a/gd/hci/facade/le_acl_manager_facade.h
+++ b/gd/hci/facade/le_acl_manager_facade.h
@@ -31,7 +31,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/hci/facade/le_acl_manager_facade.proto b/gd/hci/facade/le_acl_manager_facade.proto
index ed1000a..20bb6c2 100644
--- a/gd/hci/facade/le_acl_manager_facade.proto
+++ b/gd/hci/facade/le_acl_manager_facade.proto
@@ -7,6 +7,8 @@
 
 service LeAclManagerFacade {
   rpc CreateConnection(bluetooth.facade.BluetoothAddressWithType) returns (stream LeConnectionEvent) {}
+  rpc CreateBackgroundAndDirectConnection(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) {}
diff --git a/gd/hci/facade/le_advertising_manager_facade.cc b/gd/hci/facade/le_advertising_manager_facade.cc
index c249da0..30dd2da 100644
--- a/gd/hci/facade/le_advertising_manager_facade.cc
+++ b/gd/hci/facade/le_advertising_manager_facade.cc
@@ -443,7 +443,7 @@
   ::bluetooth::grpc::GrpcEventQueue<AddressMsg> address_events_{"address events"};
 };
 
-void LeAdvertisingManagerFacadeModule::ListDependencies(ModuleList* list) {
+void LeAdvertisingManagerFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<hci::LeAdvertisingManager>();
 }
diff --git a/gd/hci/facade/le_advertising_manager_facade.h b/gd/hci/facade/le_advertising_manager_facade.h
index 721132e..d6af43e 100644
--- a/gd/hci/facade/le_advertising_manager_facade.h
+++ b/gd/hci/facade/le_advertising_manager_facade.h
@@ -29,7 +29,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
 
@@ -41,4 +41,4 @@
 
 }  // namespace facade
 }  // namespace hci
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/hci/facade/le_initiator_address_facade.cc b/gd/hci/facade/le_initiator_address_facade.cc
index d2f7bce..c19b3db 100644
--- a/gd/hci/facade/le_initiator_address_facade.cc
+++ b/gd/hci/facade/le_initiator_address_facade.cc
@@ -101,7 +101,7 @@
   ::bluetooth::os::Handler* facade_handler_;
 };
 
-void LeInitiatorAddressFacadeModule::ListDependencies(ModuleList* list) {
+void LeInitiatorAddressFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<AclManager>();
 }
diff --git a/gd/hci/facade/le_initiator_address_facade.h b/gd/hci/facade/le_initiator_address_facade.h
index 9e1a42d..28c8c64 100644
--- a/gd/hci/facade/le_initiator_address_facade.h
+++ b/gd/hci/facade/le_initiator_address_facade.h
@@ -31,7 +31,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/hci/facade/le_scanning_manager_facade.cc b/gd/hci/facade/le_scanning_manager_facade.cc
index cb9d263..8842517 100644
--- a/gd/hci/facade/le_scanning_manager_facade.cc
+++ b/gd/hci/facade/le_scanning_manager_facade.cc
@@ -152,7 +152,7 @@
   ::bluetooth::grpc::GrpcEventQueue<ScanningCallbackMsg> callback_events_{"callback events"};
 };
 
-void LeScanningManagerFacadeModule::ListDependencies(ModuleList* list) {
+void LeScanningManagerFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<hci::LeScanningManager>();
 }
diff --git a/gd/hci/facade/le_scanning_manager_facade.h b/gd/hci/facade/le_scanning_manager_facade.h
index 8731bd0..95ec2cf 100644
--- a/gd/hci/facade/le_scanning_manager_facade.h
+++ b/gd/hci/facade/le_scanning_manager_facade.h
@@ -29,7 +29,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
 
@@ -41,4 +41,4 @@
 
 }  // namespace facade
 }  // namespace hci
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/hci/fuzz/fuzz_hci_layer.h b/gd/hci/fuzz/fuzz_hci_layer.h
index 3e99c3f..41ee39d 100644
--- a/gd/hci/fuzz/fuzz_hci_layer.h
+++ b/gd/hci/fuzz/fuzz_hci_layer.h
@@ -136,7 +136,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const override {}
   void Start() override;
   void Stop() override;
 
diff --git a/gd/hci/fuzz/hci_layer_fuzz_client.h b/gd/hci/fuzz/hci_layer_fuzz_client.h
index a5f2367..16763ce 100644
--- a/gd/hci/fuzz/hci_layer_fuzz_client.h
+++ b/gd/hci/fuzz/hci_layer_fuzz_client.h
@@ -40,7 +40,7 @@
 
   void injectArbitrary(FuzzedDataProvider& fdp);
 
-  void ListDependencies(ModuleList* list) override {
+  void ListDependencies(ModuleList* list) const override {
     list->add<hci::HciLayer>();
   }
 
diff --git a/gd/hci/hci_layer.cc b/gd/hci/hci_layer.cc
index 1952f16..5139836 100644
--- a/gd/hci/hci_layer.cc
+++ b/gd/hci/hci_layer.cc
@@ -551,7 +551,7 @@
 
 const ModuleFactory HciLayer::Factory = ModuleFactory([]() { return new HciLayer(); });
 
-void HciLayer::ListDependencies(ModuleList* list) {
+void HciLayer::ListDependencies(ModuleList* list) const {
   list->add<hal::HciHal>();
   list->add<storage::StorageModule>();
 }
diff --git a/gd/hci/hci_layer.h b/gd/hci/hci_layer.h
index d4f4ea7..d4830b1 100644
--- a/gd/hci/hci_layer.h
+++ b/gd/hci/hci_layer.h
@@ -103,7 +103,7 @@
 
  protected:
   // LINT.ThenChange(fuzz/fuzz_hci_layer.h)
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/hci/hci_layer_test.cc b/gd/hci/hci_layer_test.cc
index 745b71a..b0aaeaa 100644
--- a/gd/hci/hci_layer_test.cc
+++ b/gd/hci/hci_layer_test.cc
@@ -162,7 +162,7 @@
 
   void Stop() {}
 
-  void ListDependencies(ModuleList*) {}
+  void ListDependencies(ModuleList*) const {}
 
   std::string ToString() const override {
     return std::string("TestHciHal");
diff --git a/gd/hci/hci_packets.pdl b/gd/hci/hci_packets.pdl
index cbacf1e..7dd0b22 100644
--- a/gd/hci/hci_packets.pdl
+++ b/gd/hci/hci_packets.pdl
@@ -1009,6 +1009,7 @@
 
 packet LinkKeyRequestReplyComplete : CommandComplete (command_op_code = LINK_KEY_REQUEST_REPLY) {
   status : ErrorCode,
+  bd_addr : Address,
 }
 
 packet LinkKeyRequestNegativeReply : SecurityCommand (op_code = LINK_KEY_REQUEST_NEGATIVE_REPLY) {
diff --git a/gd/hci/le_address_manager.cc b/gd/hci/le_address_manager.cc
index a82bfb3..c7600c5 100644
--- a/gd/hci/le_address_manager.cc
+++ b/gd/hci/le_address_manager.cc
@@ -381,10 +381,22 @@
     Address peer_identity_address,
     const std::array<uint8_t, 16>& peer_irk,
     const std::array<uint8_t, 16>& local_irk) {
+  // Disable Address resolution
+  auto disable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::DISABLED);
+  Command disable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(disable_builder)};
+  cached_commands_.push(std::move(disable));
+
   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::push_command, std::move(command)).Invoke();
+  cached_commands_.push(std::move(command));
+
+  // Enable Address resolution
+  auto enable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::ENABLED);
+  Command enable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(enable_builder)};
+  cached_commands_.push(std::move(enable));
+
+  pause_registered_clients();
 }
 
 void LeAddressManager::RemoveDeviceFromConnectList(
@@ -396,10 +408,22 @@
 
 void LeAddressManager::RemoveDeviceFromResolvingList(
     PeerAddressType peer_identity_address_type, Address peer_identity_address) {
+  // Disable Address resolution
+  auto disable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::DISABLED);
+  Command disable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(disable_builder)};
+  cached_commands_.push(std::move(disable));
+
   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::push_command, std::move(command)).Invoke();
+  cached_commands_.push(std::move(command));
+
+  // Enable Address resolution
+  auto enable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::ENABLED);
+  Command enable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(enable_builder)};
+  cached_commands_.push(std::move(enable));
+
+  pause_registered_clients();
 }
 
 void LeAddressManager::ClearConnectList() {
@@ -409,9 +433,21 @@
 }
 
 void LeAddressManager::ClearResolvingList() {
+  // Disable Address resolution
+  auto disable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::DISABLED);
+  Command disable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(disable_builder)};
+  cached_commands_.push(std::move(disable));
+
   auto packet_builder = hci::LeClearResolvingListBuilder::Create();
   Command command = {CommandType::CLEAR_RESOLVING_LIST, std::move(packet_builder)};
-  handler_->BindOnceOn(this, &LeAddressManager::push_command, std::move(command)).Invoke();
+  cached_commands_.push(std::move(command));
+
+  // Enable Address resolution
+  auto enable_builder = hci::LeSetAddressResolutionEnableBuilder::Create(hci::Enable::ENABLED);
+  Command enable = {CommandType::SET_ADDRESS_RESOLUTION_ENABLE, std::move(enable_builder)};
+  cached_commands_.push(std::move(enable));
+
+  pause_registered_clients();
 }
 
 void LeAddressManager::OnCommandComplete(bluetooth::hci::CommandCompleteView view) {
diff --git a/gd/hci/le_address_manager.h b/gd/hci/le_address_manager.h
index df591d2..9b54f0e 100644
--- a/gd/hci/le_address_manager.h
+++ b/gd/hci/le_address_manager.h
@@ -104,7 +104,8 @@
     CLEAR_CONNECT_LIST,
     ADD_DEVICE_TO_RESOLVING_LIST,
     REMOVE_DEVICE_FROM_RESOLVING_LIST,
-    CLEAR_RESOLVING_LIST
+    CLEAR_RESOLVING_LIST,
+    SET_ADDRESS_RESOLUTION_ENABLE
   };
 
   struct Command {
diff --git a/gd/hci/le_address_manager_test.cc b/gd/hci/le_address_manager_test.cc
index 237a30c..6085233 100644
--- a/gd/hci/le_address_manager_test.cc
+++ b/gd/hci/le_address_manager_test.cc
@@ -104,7 +104,7 @@
     command_complete_callbacks.pop_front();
   }
 
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const {}
   void Start() override {}
   void Stop() override {}
 
diff --git a/gd/hci/le_advertising_manager.cc b/gd/hci/le_advertising_manager.cc
index c875563..fb868c9 100644
--- a/gd/hci/le_advertising_manager.cc
+++ b/gd/hci/le_advertising_manager.cc
@@ -1172,7 +1172,7 @@
   pimpl_ = std::make_unique<impl>(this);
 }
 
-void LeAdvertisingManager::ListDependencies(ModuleList* list) {
+void LeAdvertisingManager::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
   list->add<hci::Controller>();
   list->add<hci::AclManager>();
diff --git a/gd/hci/le_advertising_manager.h b/gd/hci/le_advertising_manager.h
index 9a1b2b3..75040fb 100644
--- a/gd/hci/le_advertising_manager.h
+++ b/gd/hci/le_advertising_manager.h
@@ -136,7 +136,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
@@ -157,4 +157,4 @@
 };
 
 }  // namespace hci
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/hci/le_scanning_manager.cc b/gd/hci/le_scanning_manager.cc
index 00428be..4017a85 100644
--- a/gd/hci/le_scanning_manager.cc
+++ b/gd/hci/le_scanning_manager.cc
@@ -1314,7 +1314,7 @@
   pimpl_ = std::make_unique<impl>(this);
 }
 
-void LeScanningManager::ListDependencies(ModuleList* list) {
+void LeScanningManager::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
   list->add<hci::VendorSpecificEventManager>();
   list->add<hci::Controller>();
diff --git a/gd/hci/le_scanning_manager.h b/gd/hci/le_scanning_manager.h
index 35c619b..9190366 100644
--- a/gd/hci/le_scanning_manager.h
+++ b/gd/hci/le_scanning_manager.h
@@ -159,7 +159,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/hci/vendor_specific_event_manager.cc b/gd/hci/vendor_specific_event_manager.cc
index 914a913..9d6171d 100644
--- a/gd/hci/vendor_specific_event_manager.cc
+++ b/gd/hci/vendor_specific_event_manager.cc
@@ -101,7 +101,7 @@
   pimpl_ = std::make_unique<impl>(this);
 }
 
-void VendorSpecificEventManager::ListDependencies(ModuleList* list) {
+void VendorSpecificEventManager::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
   list->add<hci::Controller>();
 }
@@ -129,4 +129,4 @@
 }
 
 }  // namespace hci
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/hci/vendor_specific_event_manager.h b/gd/hci/vendor_specific_event_manager.h
index a0535aa..7e0f333 100644
--- a/gd/hci/vendor_specific_event_manager.h
+++ b/gd/hci/vendor_specific_event_manager.h
@@ -32,7 +32,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
@@ -47,4 +47,4 @@
 };
 
 }  // namespace hci
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/iso/facade.cc b/gd/iso/facade.cc
index 9b5c88e..1f2f8fe 100644
--- a/gd/iso/facade.cc
+++ b/gd/iso/facade.cc
@@ -205,7 +205,7 @@
   ::bluetooth::os::Handler* iso_handler_;
 };
 
-void IsoModuleFacadeModule::ListDependencies(ModuleList* list) {
+void IsoModuleFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<IsoModule>();
   list->add<AclManager>();
diff --git a/gd/iso/facade.h b/gd/iso/facade.h
index 266bb09..a5a5d8d 100644
--- a/gd/iso/facade.h
+++ b/gd/iso/facade.h
@@ -28,7 +28,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/iso/iso_module.cc b/gd/iso/iso_module.cc
index 3678841..1b09967 100644
--- a/gd/iso/iso_module.cc
+++ b/gd/iso/iso_module.cc
@@ -41,7 +41,7 @@
   internal::IsoManagerImpl iso_manager_impl{iso_handler_, hci_layer_, controller_};
 };
 
-void IsoModule::ListDependencies(ModuleList* list) {
+void IsoModule::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
   list->add<hci::Controller>();
 }
@@ -63,4 +63,4 @@
 }
 
 }  // namespace iso
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/iso/iso_module.h b/gd/iso/iso_module.h
index a7e24d3..6db880c 100644
--- a/gd/iso/iso_module.h
+++ b/gd/iso/iso_module.h
@@ -36,7 +36,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/l2cap/classic/facade.cc b/gd/l2cap/classic/facade.cc
index 57d8255..9ec6456 100644
--- a/gd/l2cap/classic/facade.cc
+++ b/gd/l2cap/classic/facade.cc
@@ -413,7 +413,7 @@
   std::set<hci::Address> outgoing_pairing_remote_devices_;
 };
 
-void L2capClassicModuleFacadeModule::ListDependencies(ModuleList* list) {
+void L2capClassicModuleFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<l2cap::classic::L2capClassicModule>();
 }
diff --git a/gd/l2cap/classic/facade.h b/gd/l2cap/classic/facade.h
index ebaee0d..f699d1e 100644
--- a/gd/l2cap/classic/facade.h
+++ b/gd/l2cap/classic/facade.h
@@ -29,7 +29,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
 
@@ -41,4 +41,4 @@
 
 }  // namespace classic
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/l2cap_classic_module.cc b/gd/l2cap/classic/l2cap_classic_module.cc
index e70f1db..5c43373 100644
--- a/gd/l2cap/classic/l2cap_classic_module.cc
+++ b/gd/l2cap/classic/l2cap_classic_module.cc
@@ -89,7 +89,7 @@
 
 L2capClassicModule::~L2capClassicModule() {}
 
-void L2capClassicModule::ListDependencies(ModuleList* list) {
+void L2capClassicModule::ListDependencies(ModuleList* list) const {
   list->add<hci::AclManager>();
 }
 
diff --git a/gd/l2cap/classic/l2cap_classic_module.h b/gd/l2cap/classic/l2cap_classic_module.h
index d775694..f22f039 100644
--- a/gd/l2cap/classic/l2cap_classic_module.h
+++ b/gd/l2cap/classic/l2cap_classic_module.h
@@ -73,7 +73,7 @@
   virtual void SetLinkPropertyListener(os::Handler* handler, LinkPropertyListener* listener);
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/l2cap/fuzz/fuzz_l2cap_classic_module.h b/gd/l2cap/fuzz/fuzz_l2cap_classic_module.h
index 8028f94..c0fe746 100755
--- a/gd/l2cap/fuzz/fuzz_l2cap_classic_module.h
+++ b/gd/l2cap/fuzz/fuzz_l2cap_classic_module.h
@@ -35,7 +35,7 @@
     return std::make_unique<FuzzDynamicChannelManager>(*impl_);
   }
 
-  void ListDependencies(ModuleList*) override {}
+  void ListDependencies(ModuleList*) const override {}
   void Start() override;
   void Stop() override;
 
diff --git a/gd/l2cap/le/facade.cc b/gd/l2cap/le/facade.cc
index 0555e9b..fa84028 100644
--- a/gd/l2cap/le/facade.cc
+++ b/gd/l2cap/le/facade.cc
@@ -420,7 +420,7 @@
   ::bluetooth::grpc::GrpcEventQueue<L2capPacket> pending_l2cap_data_{"FetchL2capData"};
 };
 
-void L2capLeModuleFacadeModule::ListDependencies(ModuleList* list) {
+void L2capLeModuleFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<l2cap::le::L2capLeModule>();
 }
diff --git a/gd/l2cap/le/facade.h b/gd/l2cap/le/facade.h
index 0f811c8..8af68c0 100644
--- a/gd/l2cap/le/facade.h
+++ b/gd/l2cap/le/facade.h
@@ -29,7 +29,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/l2cap/le/l2cap_le_module.cc b/gd/l2cap/le/l2cap_le_module.cc
index 02f4408..50db2f8 100644
--- a/gd/l2cap/le/l2cap_le_module.cc
+++ b/gd/l2cap/le/l2cap_le_module.cc
@@ -69,7 +69,7 @@
 L2capLeModule::L2capLeModule() {}
 L2capLeModule::~L2capLeModule() {}
 
-void L2capLeModule::ListDependencies(ModuleList* list) {
+void L2capLeModule::ListDependencies(ModuleList* list) const {
   list->add<hci::AclManager>();
 }
 
diff --git a/gd/l2cap/le/l2cap_le_module.h b/gd/l2cap/le/l2cap_le_module.h
index f81c396..05cabd2 100644
--- a/gd/l2cap/le/l2cap_le_module.h
+++ b/gd/l2cap/le/l2cap_le_module.h
@@ -54,7 +54,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/module.h b/gd/module.h
index 083d7b8..a3c0564 100644
--- a/gd/module.h
+++ b/gd/module.h
@@ -80,7 +80,7 @@
   virtual ~Module() = default;
  protected:
   // Populate the provided list with modules that must start before yours
-  virtual void ListDependencies(ModuleList* list) = 0;
+  virtual void ListDependencies(ModuleList* list) const = 0;
 
   // You can grab your started dependencies during or after this call
   // using GetDependency(), or access the module registry via GetModuleRegistry()
diff --git a/gd/module_unittest.cc b/gd/module_unittest.cc
index f940016..975c04a 100644
--- a/gd/module_unittest.cc
+++ b/gd/module_unittest.cc
@@ -53,8 +53,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override {
-  }
+  void ListDependencies(ModuleList* list) const {}
 
   void Start() override {
     // A module is not considered started until Start() finishes
@@ -83,7 +82,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override {
+  void ListDependencies(ModuleList* list) const {
     list->add<TestModuleNoDependency>();
   }
 
@@ -117,8 +116,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override {
-  }
+  void ListDependencies(ModuleList* list) const {}
 
   void Start() override {
     // A module is not considered started until Start() finishes
@@ -144,7 +142,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override {
+  void ListDependencies(ModuleList* list) const {
     list->add<TestModuleOneDependency>();
     list->add<TestModuleNoDependencyTwo>();
   }
@@ -183,7 +181,7 @@
   std::string test_string_{"Initial Test String"};
 
  protected:
-  void ListDependencies(ModuleList* list) override {
+  void ListDependencies(ModuleList* list) const {
     list->add<TestModuleNoDependency>();
   }
 
diff --git a/gd/neighbor/connectability.cc b/gd/neighbor/connectability.cc
index d9f2d90..739367c 100644
--- a/gd/neighbor/connectability.cc
+++ b/gd/neighbor/connectability.cc
@@ -89,7 +89,7 @@
 /**
  * Module stuff
  */
-void neighbor::ConnectabilityModule::ListDependencies(ModuleList* list) {
+void neighbor::ConnectabilityModule::ListDependencies(ModuleList* list) const {
   list->add<neighbor::ScanModule>();
 }
 
diff --git a/gd/neighbor/connectability.h b/gd/neighbor/connectability.h
index 777dfce..eebf741 100644
--- a/gd/neighbor/connectability.h
+++ b/gd/neighbor/connectability.h
@@ -34,7 +34,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   std::string ToString() const override {
diff --git a/gd/neighbor/discoverability.cc b/gd/neighbor/discoverability.cc
index abaa4a7..20d609b 100644
--- a/gd/neighbor/discoverability.cc
+++ b/gd/neighbor/discoverability.cc
@@ -179,7 +179,7 @@
 /**
  * Module stuff
  */
-void neighbor::DiscoverabilityModule::ListDependencies(ModuleList* list) {
+void neighbor::DiscoverabilityModule::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
   list->add<neighbor::ScanModule>();
 }
diff --git a/gd/neighbor/discoverability.h b/gd/neighbor/discoverability.h
index 9932662..cce4b87 100644
--- a/gd/neighbor/discoverability.h
+++ b/gd/neighbor/discoverability.h
@@ -38,7 +38,7 @@
   ~DiscoverabilityModule();
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   std::string ToString() const override {
diff --git a/gd/neighbor/facade/facade.cc b/gd/neighbor/facade/facade.cc
index 1a7aef0..31f7761 100644
--- a/gd/neighbor/facade/facade.cc
+++ b/gd/neighbor/facade/facade.cc
@@ -212,7 +212,7 @@
   ::bluetooth::grpc::GrpcEventQueue<RemoteNameResponseMsg> pending_remote_names_{"RemoteNameResponses"};
 };
 
-void NeighborFacadeModule::ListDependencies(ModuleList* list) {
+void NeighborFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<ConnectabilityModule>();
   list->add<DiscoverabilityModule>();
diff --git a/gd/neighbor/facade/facade.h b/gd/neighbor/facade/facade.h
index 4f4549c..53d7b32 100644
--- a/gd/neighbor/facade/facade.h
+++ b/gd/neighbor/facade/facade.h
@@ -36,7 +36,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/neighbor/inquiry.cc b/gd/neighbor/inquiry.cc
index 332a047..9eceebb 100644
--- a/gd/neighbor/inquiry.cc
+++ b/gd/neighbor/inquiry.cc
@@ -487,7 +487,7 @@
 /**
  * Module methods here
  */
-void neighbor::InquiryModule::ListDependencies(ModuleList* list) {
+void neighbor::InquiryModule::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
 }
 
diff --git a/gd/neighbor/inquiry.h b/gd/neighbor/inquiry.h
index c671278..1482af5 100644
--- a/gd/neighbor/inquiry.h
+++ b/gd/neighbor/inquiry.h
@@ -70,7 +70,7 @@
   ~InquiryModule();
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   std::string ToString() const override {
diff --git a/gd/neighbor/inquiry_test.cc b/gd/neighbor/inquiry_test.cc
index 80ac012..3833e21 100644
--- a/gd/neighbor/inquiry_test.cc
+++ b/gd/neighbor/inquiry_test.cc
@@ -294,7 +294,7 @@
     inquiry_result_callback_.Invoke(std::move(view));
   }
 
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const {}
   void Start() override {}
   void Stop() override {}
 
diff --git a/gd/neighbor/name.cc b/gd/neighbor/name.cc
index 3a7cb64..18acf2e 100644
--- a/gd/neighbor/name.cc
+++ b/gd/neighbor/name.cc
@@ -231,7 +231,7 @@
 /**
  * Module methods here
  */
-void neighbor::NameModule::ListDependencies(ModuleList* list) {
+void neighbor::NameModule::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
 }
 
diff --git a/gd/neighbor/name.h b/gd/neighbor/name.h
index 3136e9c..233757d 100644
--- a/gd/neighbor/name.h
+++ b/gd/neighbor/name.h
@@ -48,7 +48,7 @@
   ~NameModule();
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   std::string ToString() const override {
diff --git a/gd/neighbor/name_db.cc b/gd/neighbor/name_db.cc
index 4239475..21d9737 100644
--- a/gd/neighbor/name_db.cc
+++ b/gd/neighbor/name_db.cc
@@ -147,7 +147,7 @@
 /**
  * Module methods here
  */
-void neighbor::NameDbModule::ListDependencies(ModuleList* list) {
+void neighbor::NameDbModule::ListDependencies(ModuleList* list) const {
   list->add<neighbor::NameModule>();
 }
 
diff --git a/gd/neighbor/name_db.h b/gd/neighbor/name_db.h
index 03e87fa..1180cff 100644
--- a/gd/neighbor/name_db.h
+++ b/gd/neighbor/name_db.h
@@ -43,7 +43,7 @@
   ~NameDbModule();
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   std::string ToString() const override {
diff --git a/gd/neighbor/page.cc b/gd/neighbor/page.cc
index 2f1d1e2..6124ae9 100644
--- a/gd/neighbor/page.cc
+++ b/gd/neighbor/page.cc
@@ -196,7 +196,7 @@
 /**
  * Module methods here
  */
-void neighbor::PageModule::ListDependencies(ModuleList* list) {
+void neighbor::PageModule::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
 }
 
diff --git a/gd/neighbor/page.h b/gd/neighbor/page.h
index 224b642..14c3ae6 100644
--- a/gd/neighbor/page.h
+++ b/gd/neighbor/page.h
@@ -46,7 +46,7 @@
   ~PageModule();
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   std::string ToString() const override {
diff --git a/gd/neighbor/scan.cc b/gd/neighbor/scan.cc
index 66bc4cc..9508725 100644
--- a/gd/neighbor/scan.cc
+++ b/gd/neighbor/scan.cc
@@ -189,7 +189,7 @@
   return pimpl_->IsPageEnabled();
 }
 
-void neighbor::ScanModule::ListDependencies(ModuleList* list) {
+void neighbor::ScanModule::ListDependencies(ModuleList* list) const {
   list->add<hci::HciLayer>();
 }
 
diff --git a/gd/neighbor/scan.h b/gd/neighbor/scan.h
index 83d73cf..e1699b4 100644
--- a/gd/neighbor/scan.h
+++ b/gd/neighbor/scan.h
@@ -38,7 +38,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   std::string ToString() const override {
diff --git a/gd/packet/parser/custom_type_checker.h b/gd/packet/parser/custom_type_checker.h
index 31b4660..e4da22b 100644
--- a/gd/packet/parser/custom_type_checker.h
+++ b/gd/packet/parser/custom_type_checker.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <array>
+#include <optional>
 
 #include "packet/bit_inserter.h"
 #include "packet/iterator.h"
diff --git a/gd/packet/parser/language_y.yy b/gd/packet/parser/language_y.yy
index 287cb06..1a79acd 100644
--- a/gd/packet/parser/language_y.yy
+++ b/gd/packet/parser/language_y.yy
@@ -1,4 +1,4 @@
-%{
+%code requires {
   #include <iostream>
   #include <vector>
   #include <list>
@@ -6,10 +6,11 @@
 
   #include "declarations.h"
   #include "logging.h"
-  #include "language_y.h"
   #include "field_list.h"
   #include "fields/all_fields.h"
-
+}
+%{
+  #include "language_y.h"
   extern int yylex(yy::parser::semantic_type*, yy::parser::location_type*, void *);
 
   ParseLocation toParseLocation(yy::parser::location_type loc) {
diff --git a/gd/rust/linux/dbus_projection/src/lib.rs b/gd/rust/linux/dbus_projection/src/lib.rs
index 9f28203..6cc3812 100644
--- a/gd/rust/linux/dbus_projection/src/lib.rs
+++ b/gd/rust/linux/dbus_projection/src/lib.rs
@@ -162,16 +162,16 @@
 macro_rules! impl_dbus_arg_enum {
     ($enum_type:ty) => {
         impl DBusArg for $enum_type {
-            type DBusType = i32;
+            type DBusType = u32;
             fn from_dbus(
-                data: i32,
+                data: u32,
                 _conn: Option<Arc<SyncConnection>>,
                 _remote: Option<dbus::strings::BusName<'static>>,
                 _disconnect_watcher: Option<
                     Arc<std::sync::Mutex<dbus_projection::DisconnectWatcher>>,
                 >,
             ) -> Result<$enum_type, Box<dyn std::error::Error>> {
-                match <$enum_type>::from_i32(data) {
+                match <$enum_type>::from_u32(data) {
                     Some(x) => Ok(x),
                     None => Err(Box::new(DBusArgError::new(String::from(format!(
                         "error converting {} to {}",
@@ -181,8 +181,8 @@
                 }
             }
 
-            fn to_dbus(data: $enum_type) -> Result<i32, Box<dyn std::error::Error>> {
-                return Ok(data.to_i32().unwrap());
+            fn to_dbus(data: $enum_type) -> Result<u32, Box<dyn std::error::Error>> {
+                return Ok(data.to_u32().unwrap());
             }
         }
     };
diff --git a/gd/rust/linux/mgmt/src/bin/btmanagerd/main.rs b/gd/rust/linux/mgmt/src/bin/btmanagerd/main.rs
index 69eeddf..6531ed5 100644
--- a/gd/rust/linux/mgmt/src/bin/btmanagerd/main.rs
+++ b/gd/rust/linux/mgmt/src/bin/btmanagerd/main.rs
@@ -42,7 +42,18 @@
     // Connect to the D-Bus system bus (this is blocking, unfortunately).
     let (resource, conn) = connection::new_system_sync()?;
 
-    let context = state_machine::start_new_state_machine_context();
+    // Determine whether to use upstart or systemd
+    let args: Vec<String> = std::env::args().collect();
+    let invoker = if args.len() > 1 {
+        match &args[1][0..] {
+            "--systemd" | "-s" => state_machine::Invoker::SystemdInvoker,
+            _ => state_machine::Invoker::UpstartInvoker,
+        }
+    } else {
+        state_machine::Invoker::UpstartInvoker
+    };
+
+    let context = state_machine::start_new_state_machine_context(invoker);
     let proxy = context.get_proxy();
     let manager_context = ManagerContext {
         proxy: proxy,
diff --git a/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs b/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs
index 0129c53..9d09a7d 100644
--- a/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs
+++ b/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs
@@ -14,7 +14,7 @@
 pub const PID_DIR: &str = "/var/run/bluetooth";
 
 #[derive(Debug, PartialEq, Copy, Clone)]
-#[repr(i32)]
+#[repr(u32)]
 pub enum State {
     Off = 0,        // Bluetooth is not running
     TurningOn = 1,  // We are not notified that the Bluetooth is running
@@ -49,17 +49,14 @@
     CommandTimeout(),
 }
 
-pub struct StateMachineContext<PM> {
+pub struct StateMachineContext {
     tx: mpsc::Sender<Message>,
     rx: mpsc::Receiver<Message>,
-    state_machine: ManagerStateMachine<PM>,
+    state_machine: ManagerStateMachine,
 }
 
-impl<PM> StateMachineContext<PM> {
-    fn new(state_machine: ManagerStateMachine<PM>) -> StateMachineContext<PM>
-    where
-        PM: ProcessManager + Send,
-    {
+impl StateMachineContext {
+    fn new(state_machine: ManagerStateMachine) -> StateMachineContext {
         let (tx, rx) = mpsc::channel::<Message>(10);
         StateMachineContext { tx: tx, rx: rx, state_machine: state_machine }
     }
@@ -69,8 +66,12 @@
     }
 }
 
-pub fn start_new_state_machine_context() -> StateMachineContext<UpstartInvoker> {
-    StateMachineContext::new(ManagerStateMachine::new_upstart())
+pub fn start_new_state_machine_context(invoker: Invoker) -> StateMachineContext {
+    match invoker {
+        Invoker::NativeInvoker => StateMachineContext::new(ManagerStateMachine::new_native()),
+        Invoker::SystemdInvoker => StateMachineContext::new(ManagerStateMachine::new_systemd()),
+        Invoker::UpstartInvoker => StateMachineContext::new(ManagerStateMachine::new_upstart()),
+    }
 }
 
 #[derive(Clone)]
@@ -165,12 +166,10 @@
     return None;
 }
 
-pub async fn mainloop<PM>(
-    mut context: StateMachineContext<PM>,
+pub async fn mainloop(
+    mut context: StateMachineContext,
     bluetooth_manager: Arc<std::sync::Mutex<Box<BluetoothManager>>>,
-) where
-    PM: ProcessManager + Send,
-{
+) {
     // Set up a command timeout listener to emit timeout messages
     let command_timeout = Arc::new(Alarm::new());
     let timeout_clone = command_timeout.clone();
@@ -445,18 +444,24 @@
     fn stop(&mut self, hci_interface: String);
 }
 
-pub struct NativeSubprocess {
+pub enum Invoker {
+    NativeInvoker,
+    SystemdInvoker,
+    UpstartInvoker,
+}
+
+pub struct NativeInvoker {
     process_container: Option<Child>,
     bluetooth_pid: u32,
 }
 
-impl NativeSubprocess {
-    pub fn new() -> NativeSubprocess {
-        NativeSubprocess { process_container: None, bluetooth_pid: 0 }
+impl NativeInvoker {
+    pub fn new() -> NativeInvoker {
+        NativeInvoker { process_container: None, bluetooth_pid: 0 }
     }
 }
 
-impl ProcessManager for NativeSubprocess {
+impl ProcessManager for NativeInvoker {
     fn start(&mut self, hci_interface: String) {
         let new_process = Command::new("/usr/bin/btadapterd")
             .arg(format!("HCI={}", hci_interface))
@@ -503,25 +508,48 @@
     }
 }
 
-struct ManagerStateMachine<PM> {
+pub struct SystemdInvoker {}
+
+impl SystemdInvoker {
+    pub fn new() -> SystemdInvoker {
+        SystemdInvoker {}
+    }
+}
+
+impl ProcessManager for SystemdInvoker {
+    fn start(&mut self, hci_interface: String) {
+        Command::new("systemctl")
+            .args(&["restart", format!("btadapterd@{}.service", hci_interface).as_str()])
+            .output()
+            .expect("failed to start bluetooth");
+    }
+
+    fn stop(&mut self, hci_interface: String) {
+        Command::new("systemctl")
+            .args(&["stop", format!("btadapterd@{}.service", hci_interface).as_str()])
+            .output()
+            .expect("failed to stop bluetooth");
+    }
+}
+
+struct ManagerStateMachine {
     state: Arc<std::sync::Mutex<State>>,
-    process_manager: PM,
+    process_manager: Box<dyn ProcessManager + Send>,
     hci_interface: i32,
     bluetooth_pid: i32,
 }
 
-impl ManagerStateMachine<NativeSubprocess> {
-    // NativeSubprocess is not used but is still useful for testing in Linux without upstart.
-    // Don't remove just yet.
-    #[allow(dead_code)]
-    pub fn new_native() -> ManagerStateMachine<NativeSubprocess> {
-        ManagerStateMachine::new(NativeSubprocess::new())
+impl ManagerStateMachine {
+    pub fn new_upstart() -> ManagerStateMachine {
+        ManagerStateMachine::new(Box::new(UpstartInvoker::new()))
     }
-}
 
-impl ManagerStateMachine<UpstartInvoker> {
-    pub fn new_upstart() -> ManagerStateMachine<UpstartInvoker> {
-        ManagerStateMachine::new(UpstartInvoker::new())
+    pub fn new_systemd() -> ManagerStateMachine {
+        ManagerStateMachine::new(Box::new(SystemdInvoker::new()))
+    }
+
+    pub fn new_native() -> ManagerStateMachine {
+        ManagerStateMachine::new(Box::new(NativeInvoker::new()))
     }
 }
 
@@ -532,11 +560,8 @@
     Noop,
 }
 
-impl<PM> ManagerStateMachine<PM>
-where
-    PM: ProcessManager + Send,
-{
-    pub fn new(process_manager: PM) -> ManagerStateMachine<PM> {
+impl ManagerStateMachine {
+    pub fn new(process_manager: Box<dyn ProcessManager + Send>) -> ManagerStateMachine {
         ManagerStateMachine {
             state: Arc::new(std::sync::Mutex::new(State::Off)),
             process_manager: process_manager,
@@ -702,7 +727,7 @@
     fn initial_state_is_off() {
         tokio::runtime::Runtime::new().unwrap().block_on(async {
             let process_manager = MockProcessManager::new();
-            let state_machine = ManagerStateMachine::new(process_manager);
+            let state_machine = ManagerStateMachine::new(Box::new(process_manager));
             assert_eq!(*state_machine.state.lock().unwrap(), State::Off);
         })
     }
@@ -711,7 +736,7 @@
     fn off_turnoff_should_noop() {
         tokio::runtime::Runtime::new().unwrap().block_on(async {
             let process_manager = MockProcessManager::new();
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_stop_bluetooth(0);
             assert_eq!(*state_machine.state.lock().unwrap(), State::Off);
         })
@@ -723,7 +748,7 @@
             let mut process_manager = MockProcessManager::new();
             // Expect to send start command
             process_manager.expect_start();
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_start_bluetooth(0);
             assert_eq!(*state_machine.state.lock().unwrap(), State::TurningOn);
         })
@@ -735,7 +760,7 @@
             let mut process_manager = MockProcessManager::new();
             // Expect to send start command just once
             process_manager.expect_start();
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_start_bluetooth(0);
             assert_eq!(state_machine.action_start_bluetooth(0), false);
         })
@@ -746,7 +771,7 @@
         tokio::runtime::Runtime::new().unwrap().block_on(async {
             let mut process_manager = MockProcessManager::new();
             process_manager.expect_start();
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_start_bluetooth(0);
             state_machine.action_on_bluetooth_started(0, 0);
             assert_eq!(*state_machine.state.lock().unwrap(), State::On);
@@ -754,13 +779,25 @@
     }
 
     #[test]
+    fn turningon_bluetooth_different_hci_started() {
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            process_manager.expect_start();
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
+            state_machine.action_start_bluetooth(1);
+            state_machine.action_on_bluetooth_started(1, 1);
+            assert_eq!(*state_machine.state.lock().unwrap(), State::On);
+        })
+    }
+
+    #[test]
     fn turningon_timeout() {
         tokio::runtime::Runtime::new().unwrap().block_on(async {
             let mut process_manager = MockProcessManager::new();
             process_manager.expect_start();
             process_manager.expect_stop();
             process_manager.expect_start(); // start bluetooth again
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_start_bluetooth(0);
             assert_eq!(
                 state_machine.action_on_command_timeout(),
@@ -777,7 +814,7 @@
             process_manager.expect_start();
             // Expect to send stop command
             process_manager.expect_stop();
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_start_bluetooth(0);
             state_machine.action_stop_bluetooth(0);
             assert_eq!(*state_machine.state.lock().unwrap(), State::Off);
@@ -791,7 +828,7 @@
             process_manager.expect_start();
             // Expect to send stop command
             process_manager.expect_stop();
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_start_bluetooth(0);
             state_machine.action_on_bluetooth_started(0, 0);
             state_machine.action_stop_bluetooth(0);
@@ -806,7 +843,7 @@
             process_manager.expect_start();
             // Expect to start again
             process_manager.expect_start();
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_start_bluetooth(0);
             state_machine.action_on_bluetooth_started(0, 0);
             assert_eq!(state_machine.action_on_bluetooth_stopped(), false);
@@ -820,7 +857,7 @@
             let mut process_manager = MockProcessManager::new();
             process_manager.expect_start();
             process_manager.expect_stop();
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_start_bluetooth(0);
             state_machine.action_on_bluetooth_started(0, 0);
             state_machine.action_stop_bluetooth(0);
@@ -836,7 +873,7 @@
             process_manager.expect_start();
             process_manager.expect_stop();
             process_manager.expect_start();
-            let mut state_machine = ManagerStateMachine::new(process_manager);
+            let mut state_machine = ManagerStateMachine::new(Box::new(process_manager));
             state_machine.action_start_bluetooth(0);
             state_machine.action_on_bluetooth_started(0, 0);
             state_machine.action_stop_bluetooth(0);
diff --git a/gd/rust/linux/stack/src/bluetooth.rs b/gd/rust/linux/stack/src/bluetooth.rs
index c6d9af7..4a02a8f 100644
--- a/gd/rust/linux/stack/src/bluetooth.rs
+++ b/gd/rust/linux/stack/src/bluetooth.rs
@@ -594,7 +594,7 @@
 
     fn remote_device_properties_changed(
         &mut self,
-        status: BtStatus,
+        _status: BtStatus,
         addr: RawAddress,
         _num_properties: i32,
         properties: Vec<BluetoothProperty>,
diff --git a/gd/rust/linux/stack/src/bluetooth_gatt.rs b/gd/rust/linux/stack/src/bluetooth_gatt.rs
index a58fb05..fb0f61c 100644
--- a/gd/rust/linux/stack/src/bluetooth_gatt.rs
+++ b/gd/rust/linux/stack/src/bluetooth_gatt.rs
@@ -443,7 +443,7 @@
 }
 
 #[derive(Debug, FromPrimitive, ToPrimitive)]
-#[repr(i32)]
+#[repr(u32)]
 /// Scan type configuration.
 pub enum ScanType {
     Active = 0,
diff --git a/gd/rust/topshim/facade/Android.bp b/gd/rust/topshim/facade/Android.bp
index 95f514b..f0e185a 100644
--- a/gd/rust/topshim/facade/Android.bp
+++ b/gd/rust/topshim/facade/Android.bp
@@ -47,6 +47,7 @@
         "libudrv-uipc",
         "libbluetooth_gd", // Gabeldorsche
         "libbluetooth_rust_interop",
+        "libbluetooth-dumpsys",
         "libflatbuffers-cpp",
     ],
     shared_libs: [
diff --git a/gd/rust/topshim/src/btif.rs b/gd/rust/topshim/src/btif.rs
index 1439a7f..c74fc31 100644
--- a/gd/rust/topshim/src/btif.rs
+++ b/gd/rust/topshim/src/btif.rs
@@ -161,7 +161,7 @@
 }
 
 #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)]
-#[repr(i32)]
+#[repr(u32)]
 pub enum BtDiscoveryState {
     Stopped = 0x0,
     Started,
diff --git a/gd/security/facade.cc b/gd/security/facade.cc
index f2baaf0..a866c10 100644
--- a/gd/security/facade.cc
+++ b/gd/security/facade.cc
@@ -538,7 +538,7 @@
   std::map<uint32_t, common::OnceCallback<void(uint32_t)>> user_passkey_callbacks_;
 };
 
-void SecurityModuleFacadeModule::ListDependencies(ModuleList* list) {
+void SecurityModuleFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<SecurityModule>();
   list->add<L2capLeModule>();
diff --git a/gd/security/facade.h b/gd/security/facade.h
index 8f060c2..207ad42 100644
--- a/gd/security/facade.h
+++ b/gd/security/facade.h
@@ -28,7 +28,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/security/security_module.cc b/gd/security/security_module.cc
index d753780..a8617b8 100644
--- a/gd/security/security_module.cc
+++ b/gd/security/security_module.cc
@@ -90,7 +90,7 @@
   }
 };
 
-void SecurityModule::ListDependencies(ModuleList* list) {
+void SecurityModule::ListDependencies(ModuleList* list) const {
   list->add<l2cap::le::L2capLeModule>();
   list->add<l2cap::classic::L2capClassicModule>();
   list->add<hci::HciLayer>();
@@ -133,4 +133,4 @@
 }
 
 }  // namespace security
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/security/security_module.h b/gd/security/security_module.h
index 18f2543..8890733 100644
--- a/gd/security/security_module.h
+++ b/gd/security/security_module.h
@@ -44,7 +44,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
 
   void Start() override;
 
diff --git a/gd/security/test/fake_hci_layer.h b/gd/security/test/fake_hci_layer.h
index 6ffd1c2..8d16e80 100644
--- a/gd/security/test/fake_hci_layer.h
+++ b/gd/security/test/fake_hci_layer.h
@@ -100,7 +100,7 @@
     registered_events_[event_code].Invoke(event);
   }
 
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const override {}
   void Start() override {}
   void Stop() override {}
 
diff --git a/gd/security/test/fake_name_db.h b/gd/security/test/fake_name_db.h
index 9a34094..91a3195 100644
--- a/gd/security/test/fake_name_db.h
+++ b/gd/security/test/fake_name_db.h
@@ -25,7 +25,7 @@
  public:
   FakeNameDbModule() {}
 
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const override {}
   void Start() override {}
   void Stop() override {}
   std::string ToString() const override {
diff --git a/gd/shim/BUILD.gn b/gd/shim/BUILD.gn
index 59dbb37..39d4601 100644
--- a/gd/shim/BUILD.gn
+++ b/gd/shim/BUILD.gn
@@ -20,7 +20,7 @@
   ]
 
   deps = [
-    "//bt/gd/dumpsys:BluetoothGeneratedDumpsysBundledSchema_h",
+    "//bt/gd/dumpsys:libbluetooth-dumpsys",
     "//bt/gd/dumpsys/bundler:BluetoothGeneratedBundlerSchema_h_bfbs",
   ]
 
diff --git a/gd/shim/dumpsys.cc b/gd/shim/dumpsys.cc
index d544cf5..33259de 100644
--- a/gd/shim/dumpsys.cc
+++ b/gd/shim/dumpsys.cc
@@ -15,11 +15,12 @@
  */
 #define LOG_TAG "bt_gd_shim"
 
+#include "dumpsys/dumpsys.h"
+
 #include <future>
 #include <string>
 
 #include "dumpsys/filter.h"
-#include "generated_dumpsys_bundled_schema.h"
 #include "module.h"
 #include "os/log.h"
 #include "os/system_properties.h"
@@ -166,7 +167,7 @@
 /**
  * Module methods
  */
-void Dumpsys::ListDependencies(ModuleList* list) {}
+void Dumpsys::ListDependencies(ModuleList* list) const {}
 
 void Dumpsys::Start() {
   pimpl_ = std::make_unique<impl>(*this, reflection_schema_);
diff --git a/gd/shim/dumpsys.h b/gd/shim/dumpsys.h
index e843db7..35c1936 100644
--- a/gd/shim/dumpsys.h
+++ b/gd/shim/dumpsys.h
@@ -41,7 +41,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override;  // Module
+  void ListDependencies(ModuleList* list) const override;  // Module
   void Start() override;                             // Module
   void Stop() override;                              // Module
   std::string ToString() const override;             // Module
diff --git a/gd/shim/facade/facade.cc b/gd/shim/facade/facade.cc
index a07329a..1c72596 100644
--- a/gd/shim/facade/facade.cc
+++ b/gd/shim/facade/facade.cc
@@ -55,7 +55,7 @@
   [[maybe_unused]] ::bluetooth::os::Handler* facade_handler_{nullptr};
 };
 
-void ShimFacadeModule::ListDependencies(ModuleList* list) {
+void ShimFacadeModule::ListDependencies(ModuleList* list) const {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<Dumpsys>();
 }
diff --git a/gd/shim/facade/facade.h b/gd/shim/facade/facade.h
index 2534532..b838351 100644
--- a/gd/shim/facade/facade.h
+++ b/gd/shim/facade/facade.h
@@ -31,7 +31,7 @@
  public:
   static const ModuleFactory Factory;
 
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   ::grpc::Service* GetService() const override;
diff --git a/gd/stack_manager_unittest.cc b/gd/stack_manager_unittest.cc
index b82fe16..824675e 100644
--- a/gd/stack_manager_unittest.cc
+++ b/gd/stack_manager_unittest.cc
@@ -35,7 +35,7 @@
   static const ModuleFactory Factory;
 
  protected:
-  void ListDependencies(ModuleList* list) override {}
+  void ListDependencies(ModuleList* list) const {}
   void Start() override {}
   void Stop() override {}
   std::string ToString() const override {
diff --git a/gd/storage/storage_module.cc b/gd/storage/storage_module.cc
index 6b4e9bc..c7ef383 100644
--- a/gd/storage/storage_module.cc
+++ b/gd/storage/storage_module.cc
@@ -136,7 +136,7 @@
   ASSERT(LegacyConfigFile::FromPath(config_backup_path_).Write(pimpl_->cache_));
 }
 
-void StorageModule::ListDependencies(ModuleList* list) {
+void StorageModule::ListDependencies(ModuleList* list) const {
   // No dependencies
 }
 
@@ -235,4 +235,4 @@
 }
 
 }  // namespace storage
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/storage/storage_module.h b/gd/storage/storage_module.h
index 082b6a5..3491796 100644
--- a/gd/storage/storage_module.h
+++ b/gd/storage/storage_module.h
@@ -106,7 +106,7 @@
   Mutation Modify();
 
  protected:
-  void ListDependencies(ModuleList* list) override;
+  void ListDependencies(ModuleList* list) const override;
   void Start() override;
   void Stop() override;
   std::string ToString() const override;
diff --git a/hci/include/hci_packet_factory.h b/hci/include/hci_packet_factory.h
index 8a6e98e..e69de29 100644
--- a/hci/include/hci_packet_factory.h
+++ b/hci/include/hci_packet_factory.h
@@ -1,54 +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 "btcore/include/event_mask.h"
-#include "stack/include/bt_hdr.h"
-
-typedef struct {
-  BT_HDR* (*make_reset)(void);
-  BT_HDR* (*make_read_buffer_size)(void);
-  BT_HDR* (*make_host_buffer_size)(uint16_t acl_size, uint8_t sco_size,
-                                   uint16_t acl_count, uint16_t sco_count);
-  BT_HDR* (*make_read_local_version_info)(void);
-  BT_HDR* (*make_read_bd_addr)(void);
-  BT_HDR* (*make_read_local_supported_commands)(void);
-  BT_HDR* (*make_read_local_extended_features)(uint8_t page_number);
-  BT_HDR* (*make_write_simple_pairing_mode)(uint8_t mode);
-  BT_HDR* (*make_write_secure_connections_host_support)(uint8_t mode);
-  BT_HDR* (*make_set_event_mask)(const bt_event_mask_t* event_mask);
-  BT_HDR* (*make_ble_write_host_support)(uint8_t supported_host,
-                                         uint8_t simultaneous_host);
-  BT_HDR* (*make_ble_read_acceptlist_size)(void);
-  BT_HDR* (*make_ble_read_buffer_size)(void);
-  BT_HDR* (*make_ble_read_buffer_size_v2)(void);
-  BT_HDR* (*make_ble_read_supported_states)(void);
-  BT_HDR* (*make_ble_read_local_supported_features)(void);
-  BT_HDR* (*make_ble_read_resolving_list_size)(void);
-  BT_HDR* (*make_ble_read_suggested_default_data_length)(void);
-  BT_HDR* (*make_ble_read_maximum_data_length)(void);
-  BT_HDR* (*make_ble_read_maximum_advertising_data_length)(void);
-  BT_HDR* (*make_ble_read_number_of_supported_advertising_sets)(void);
-  BT_HDR* (*make_ble_read_periodic_advertiser_list_size)(void);
-  BT_HDR* (*make_ble_set_event_mask)(const bt_event_mask_t* event_mask);
-  BT_HDR* (*make_ble_set_host_features)(uint8_t bit_number, uint8_t bit_value);
-  BT_HDR* (*make_read_local_supported_codecs)(void);
-} hci_packet_factory_t;
-
-const hci_packet_factory_t* hci_packet_factory_get_interface();
diff --git a/hci/src/hci_packet_factory.cc b/hci/src/hci_packet_factory.cc
index 5bafe83..e69de29 100644
--- a/hci/src/hci_packet_factory.cc
+++ b/hci/src/hci_packet_factory.cc
@@ -1,253 +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.
- *
- ******************************************************************************/
-
-#include <base/logging.h>
-
-#include "check.h"
-#include "hci/include/buffer_allocator.h"
-#include "hci_layer.h"
-#include "hci_packet_factory.h"
-#include "hcidefs.h"
-#include "osi/include/allocator.h"
-#include "stack/include/bt_hdr.h"
-
-#define HCI_COMMAND_PREAMBLE_SIZE 3
-
-static const allocator_t* buffer_allocator;
-
-static BT_HDR* make_packet(size_t data_size);
-static BT_HDR* make_command_no_params(uint16_t opcode);
-static BT_HDR* make_command(uint16_t opcode, size_t parameter_size,
-                            uint8_t** stream_out);
-
-// Interface functions
-
-static BT_HDR* make_reset(void) { return make_command_no_params(HCI_RESET); }
-
-static BT_HDR* make_read_buffer_size(void) {
-  return make_command_no_params(HCI_READ_BUFFER_SIZE);
-}
-
-static BT_HDR* make_host_buffer_size(uint16_t acl_size, uint8_t sco_size,
-                                     uint16_t acl_count, uint16_t sco_count) {
-  uint8_t* stream;
-  const uint8_t parameter_size = 2 + 1 + 2 + 2;  // from each of the parameters
-  BT_HDR* packet = make_command(HCI_HOST_BUFFER_SIZE, parameter_size, &stream);
-
-  UINT16_TO_STREAM(stream, acl_size);
-  UINT8_TO_STREAM(stream, sco_size);
-  UINT16_TO_STREAM(stream, acl_count);
-  UINT16_TO_STREAM(stream, sco_count);
-  return packet;
-}
-
-static BT_HDR* make_read_local_version_info(void) {
-  return make_command_no_params(HCI_READ_LOCAL_VERSION_INFO);
-}
-
-static BT_HDR* make_read_bd_addr(void) {
-  return make_command_no_params(HCI_READ_BD_ADDR);
-}
-
-static BT_HDR* make_read_local_supported_commands(void) {
-  return make_command_no_params(HCI_READ_LOCAL_SUPPORTED_CMDS);
-}
-
-static BT_HDR* make_read_local_extended_features(uint8_t page_number) {
-  uint8_t* stream;
-  const uint8_t parameter_size = 1;
-  BT_HDR* packet =
-      make_command(HCI_READ_LOCAL_EXT_FEATURES, parameter_size, &stream);
-
-  UINT8_TO_STREAM(stream, page_number);
-  return packet;
-}
-
-static BT_HDR* make_write_simple_pairing_mode(uint8_t mode) {
-  uint8_t* stream;
-  const uint8_t parameter_size = 1;
-  BT_HDR* packet =
-      make_command(HCI_WRITE_SIMPLE_PAIRING_MODE, parameter_size, &stream);
-
-  UINT8_TO_STREAM(stream, mode);
-  return packet;
-}
-
-static BT_HDR* make_write_secure_connections_host_support(uint8_t mode) {
-  uint8_t* stream;
-  const uint8_t parameter_size = 1;
-  BT_HDR* packet =
-      make_command(HCI_WRITE_SECURE_CONNS_SUPPORT, parameter_size, &stream);
-
-  UINT8_TO_STREAM(stream, mode);
-  return packet;
-}
-
-static BT_HDR* make_set_event_mask(const bt_event_mask_t* event_mask) {
-  uint8_t* stream;
-  uint8_t parameter_size = sizeof(bt_event_mask_t);
-  BT_HDR* packet = make_command(HCI_SET_EVENT_MASK, parameter_size, &stream);
-
-  ARRAY8_TO_STREAM(stream, event_mask->as_array);
-  return packet;
-}
-
-static BT_HDR* make_ble_write_host_support(uint8_t supported_host,
-                                           uint8_t simultaneous_host) {
-  uint8_t* stream;
-  const uint8_t parameter_size = 1 + 1;
-  BT_HDR* packet =
-      make_command(HCI_WRITE_LE_HOST_SUPPORT, parameter_size, &stream);
-
-  UINT8_TO_STREAM(stream, supported_host);
-  UINT8_TO_STREAM(stream, simultaneous_host);
-  return packet;
-}
-
-static BT_HDR* make_ble_read_acceptlist_size(void) {
-  return make_command_no_params(HCI_BLE_READ_ACCEPTLIST_SIZE);
-}
-
-static BT_HDR* make_ble_read_buffer_size(void) {
-  return make_command_no_params(HCI_BLE_READ_BUFFER_SIZE);
-}
-
-static BT_HDR* make_ble_read_buffer_size_v2(void) {
-  return make_command_no_params(HCI_BLE_READ_BUFFER_SIZE_V2);
-}
-
-static BT_HDR* make_ble_read_supported_states(void) {
-  return make_command_no_params(HCI_BLE_READ_SUPPORTED_STATES);
-}
-
-static BT_HDR* make_ble_read_local_supported_features(void) {
-  return make_command_no_params(HCI_BLE_READ_LOCAL_SPT_FEAT);
-}
-
-static BT_HDR* make_ble_read_resolving_list_size(void) {
-  return make_command_no_params(HCI_BLE_READ_RESOLVING_LIST_SIZE);
-}
-
-static BT_HDR* make_ble_read_suggested_default_data_length(void) {
-  return make_command_no_params(HCI_BLE_READ_DEFAULT_DATA_LENGTH);
-}
-
-static BT_HDR* make_ble_read_maximum_data_length(void) {
-  return make_command_no_params(HCI_BLE_READ_MAXIMUM_DATA_LENGTH);
-}
-
-static BT_HDR* make_ble_read_maximum_advertising_data_length(void) {
-  return make_command_no_params(HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH);
-}
-
-static BT_HDR* make_ble_read_number_of_supported_advertising_sets(void) {
-  return make_command_no_params(
-      HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS);
-}
-
-static BT_HDR* make_ble_read_periodic_advertiser_list_size(void) {
-  return make_command_no_params(HCI_BLE_READ_PERIODIC_ADVERTISER_LIST_SIZE);
-}
-
-static BT_HDR* make_read_local_supported_codecs(void) {
-  return make_command_no_params(HCI_READ_LOCAL_SUPPORTED_CODECS);
-}
-
-static BT_HDR* make_ble_set_event_mask(const bt_event_mask_t* event_mask) {
-  uint8_t* stream;
-  uint8_t parameter_size = sizeof(bt_event_mask_t);
-  BT_HDR* packet =
-      make_command(HCI_BLE_SET_EVENT_MASK, parameter_size, &stream);
-
-  ARRAY8_TO_STREAM(stream, event_mask->as_array);
-  return packet;
-}
-
-static BT_HDR* make_ble_set_host_features(uint8_t bit_number,
-                                          uint8_t bit_value) {
-  uint8_t* stream;
-  uint8_t parameter_size = sizeof(bit_number) + sizeof(bit_value);
-  BT_HDR* packet =
-      make_command(HCI_LE_SET_HOST_FEATURE, parameter_size, &stream);
-
-  UINT8_TO_STREAM(stream, bit_number);
-  UINT8_TO_STREAM(stream, bit_value);
-
-  return packet;
-}
-
-// Internal functions
-
-static BT_HDR* make_command_no_params(uint16_t opcode) {
-  return make_command(opcode, 0, NULL);
-}
-
-static BT_HDR* make_command(uint16_t opcode, size_t parameter_size,
-                            uint8_t** stream_out) {
-  BT_HDR* packet = make_packet(HCI_COMMAND_PREAMBLE_SIZE + parameter_size);
-
-  uint8_t* stream = packet->data;
-  UINT16_TO_STREAM(stream, opcode);
-  UINT8_TO_STREAM(stream, parameter_size);
-
-  if (stream_out != NULL) *stream_out = stream;
-
-  return packet;
-}
-
-static BT_HDR* make_packet(size_t data_size) {
-  BT_HDR* ret = (BT_HDR*)buffer_allocator->alloc(sizeof(BT_HDR) + data_size);
-  CHECK(ret);
-  ret->event = 0;
-  ret->offset = 0;
-  ret->layer_specific = 0;
-  ret->len = data_size;
-  return ret;
-}
-
-static const hci_packet_factory_t interface = {
-    make_reset,
-    make_read_buffer_size,
-    make_host_buffer_size,
-    make_read_local_version_info,
-    make_read_bd_addr,
-    make_read_local_supported_commands,
-    make_read_local_extended_features,
-    make_write_simple_pairing_mode,
-    make_write_secure_connections_host_support,
-    make_set_event_mask,
-    make_ble_write_host_support,
-    make_ble_read_acceptlist_size,
-    make_ble_read_buffer_size,
-    make_ble_read_buffer_size_v2,
-    make_ble_read_supported_states,
-    make_ble_read_local_supported_features,
-    make_ble_read_resolving_list_size,
-    make_ble_read_suggested_default_data_length,
-    make_ble_read_maximum_data_length,
-    make_ble_read_maximum_advertising_data_length,
-    make_ble_read_number_of_supported_advertising_sets,
-    make_ble_read_periodic_advertiser_list_size,
-    make_ble_set_event_mask,
-    make_ble_set_host_features,
-    make_read_local_supported_codecs};
-
-const hci_packet_factory_t* hci_packet_factory_get_interface() {
-  buffer_allocator = buffer_allocator_get_interface();
-  return &interface;
-}
diff --git a/internal_include/bt_common.h b/internal_include/bt_common.h
deleted file mode 100644
index 31de238..0000000
--- a/internal_include/bt_common.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2015 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 "bt_target.h"
-#include "osi/include/allocator.h"
-#include "osi/include/compat.h"
-#include "stack/include/bt_types.h"
diff --git a/internal_include/bt_target.h b/internal_include/bt_target.h
index dd8c74e..8784a02 100644
--- a/internal_include/bt_target.h
+++ b/internal_include/bt_target.h
@@ -1049,4 +1049,12 @@
 
 #include "bt_trace.h"
 
+#ifndef BTM_DELAY_AUTH_MS
+#define BTM_DELAY_AUTH_MS 0
+#endif
+
+#ifndef BTM_DISABLE_CONCURRENT_PEER_AUTH
+#define BTM_DISABLE_CONCURRENT_PEER_AUTH FALSE
+#endif
+
 #endif /* BT_TARGET_H */
diff --git a/main/Android.bp b/main/Android.bp
index 8b8762c..5e4ff91 100644
--- a/main/Android.bp
+++ b/main/Android.bp
@@ -107,6 +107,7 @@
         "libudrv-uipc",
         "libprotobuf-cpp-lite",
         "libbluetooth_gd", // Gabeldorsche
+        "libbluetooth-dumpsys",
         "libbluetooth_rust_interop",
     ],
     whole_static_libs: [
@@ -243,6 +244,7 @@
         "test/main_shim_test.cc",
     ],
     static_libs: [
+        "libbluetooth-dumpsys",
         "libbt-common",
         "libbt-protos-lite",
         "libgmock",
@@ -268,7 +270,6 @@
     generated_headers: [
         "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
-        "BluetoothGeneratedDumpsysBundledSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
 }
diff --git a/main/shim/controller.cc b/main/shim/controller.cc
index 13313a8..264c456 100644
--- a/main/shim/controller.cc
+++ b/main/shim/controller.cc
@@ -401,3 +401,7 @@
   }
   return &interface;
 }
+
+void bluetooth::shim::controller_clear_event_mask() {
+  bluetooth::shim::GetController()->SetEventMask(0);
+}
diff --git a/main/shim/controller.h b/main/shim/controller.h
index 6b77982..c845141 100644
--- a/main/shim/controller.h
+++ b/main/shim/controller.h
@@ -25,5 +25,7 @@
 
 const controller_t* controller_get_interface();
 
+void controller_clear_event_mask();
+
 }  // namespace shim
 }  // namespace bluetooth
diff --git a/main/shim/hci_layer.cc b/main/shim/hci_layer.cc
index 8d5c638..1d356af 100644
--- a/main/shim/hci_layer.cc
+++ b/main/shim/hci_layer.cc
@@ -406,7 +406,8 @@
       handle_with_flags >> 12 & 0b11);
   auto bc_flag =
       static_cast<bluetooth::hci::BroadcastFlag>(handle_with_flags >> 14);
-  uint16_t handle = handle_with_flags & 0xEFF;
+  uint16_t handle = handle_with_flags & 0xFFF;
+  ASSERT_LOG(handle <= 0xEFF, "Require handle <= 0xEFF, but is 0x%X", handle);
   length -= 2;
   // skip data total length
   stream += 2;
@@ -421,7 +422,8 @@
 static void transmit_sco_fragment(const uint8_t* stream, size_t length) {
   uint16_t handle_with_flags;
   STREAM_TO_UINT16(handle_with_flags, stream);
-  uint16_t handle = handle_with_flags & 0xEFF;
+  uint16_t handle = handle_with_flags & 0xFFF;
+  ASSERT_LOG(handle <= 0xEFF, "Require handle <= 0xEFF, but is 0x%X", handle);
   length -= 2;
   // skip data total length
   stream += 1;
@@ -442,7 +444,8 @@
       handle_with_flags >> 12 & 0b11);
   auto ts_flag =
       static_cast<bluetooth::hci::TimeStampFlag>(handle_with_flags >> 14);
-  uint16_t handle = handle_with_flags & 0xEFF;
+  uint16_t handle = handle_with_flags & 0xFFF;
+  ASSERT_LOG(handle <= 0xEFF, "Require handle <= 0xEFF, but is 0x%X", handle);
   length -= 2;
   // skip data total length
   stream += 2;
diff --git a/main/test/main_shim_test.cc b/main/test/main_shim_test.cc
index 117c6e0..7f40241 100644
--- a/main/test/main_shim_test.cc
+++ b/main/test/main_shim_test.cc
@@ -169,9 +169,6 @@
   return acl_interface;
 }
 
-const hci_packet_factory_t* hci_packet_factory_get_interface() {
-  return nullptr;
-}
 const hci_packet_parser_t* hci_packet_parser_get_interface() { return nullptr; }
 const hci_t* hci_layer_get_interface() { return nullptr; }
 const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; }
diff --git a/profile/avrcp/device.cc b/profile/avrcp/device.cc
index c5cdc55..708fc24 100644
--- a/profile/avrcp/device.cc
+++ b/profile/avrcp/device.cc
@@ -440,7 +440,6 @@
                                               std::string curr_song_id,
                                               std::vector<SongInfo> song_list) {
   DEVICE_VLOG(1) << __func__;
-  uint64_t uid = 0;
 
   if (interim) {
     track_changed_ = Notification(true, label);
@@ -449,9 +448,32 @@
     return;
   }
 
+  if (!interim) {
+    if (curr_song_id.empty()) {
+      // CHANGED response is only defined when there is media selected
+      // for playing.
+      return;
+    }
+    active_labels_.erase(label);
+    track_changed_ = Notification(false, 0);
+  }
+
+  // Case for browsing not supported;
+  // PTS BV-04-C and BV-5-C assume browsing not supported
+  if (stack_config_get_interface()->get_pts_avrcp_test()) {
+    DEVICE_LOG(WARNING) << __func__ << ": pts test mode";
+    uint64_t uid = curr_song_id.empty() ? 0xffffffffffffffff : 0;
+    auto response =
+        RegisterNotificationResponseBuilder::MakeTrackChangedBuilder(interim,
+                                                                     uid);
+    send_message_cb_.Run(label, false, std::move(response));
+    return;
+  }
+
   // Anytime we use the now playing list, update our map so that its always
   // current
   now_playing_ids_.clear();
+  uint64_t uid = 0;
   for (const SongInfo& song : song_list) {
     now_playing_ids_.insert(song.media_id);
     if (curr_song_id == song.media_id) {
@@ -461,22 +483,14 @@
     }
   }
 
-  if (curr_song_id == "") {
-    DEVICE_LOG(WARNING) << "Empty media ID";
-    uid = 0;
-    if (stack_config_get_interface()->get_pts_avrcp_test()) {
-      DEVICE_LOG(WARNING) << __func__ << ": pts test mode";
-      uid = 0xffffffffffffffff;
-    }
+  if (uid == 0) {
+    // uid 0 is not valid here when browsing is supported
+    DEVICE_LOG(ERROR) << "No match for media ID found";
   }
 
   auto response = RegisterNotificationResponseBuilder::MakeTrackChangedBuilder(
       interim, uid);
   send_message_cb_.Run(label, false, std::move(response));
-  if (!interim) {
-    active_labels_.erase(label);
-    track_changed_ = Notification(false, 0);
-  }
 }
 
 void Device::PlaybackStatusNotificationResponse(uint8_t label, bool interim,
diff --git a/stack/Android.bp b/stack/Android.bp
index b5f40a7..1a074ea 100644
--- a/stack/Android.bp
+++ b/stack/Android.bp
@@ -257,6 +257,7 @@
     ],
     static_libs: [
         "libbt-audio-hal-interface",
+        "libbluetooth-dumpsys",
         "libbtcore",
         "libbt-bta",
         "libbt-stack",
@@ -383,9 +384,7 @@
         "test/stack_smp_test.cc",
     ],
     generated_headers: [
-        "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
-        "BluetoothGeneratedDumpsysBundledSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
     shared_libs: [
@@ -424,6 +423,9 @@
         "test/ble_advertiser_test.cc",
     ],
     shared_libs: [
+        "android.system.suspend.control-V1-ndk",
+        "libbinder_ndk",
+        "libcrypto",
         "libcutils",
     ],
     static_libs: [
@@ -775,7 +777,6 @@
         "system/bt/vnd/ble",
     ],
     generated_headers: [
-        "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
@@ -957,7 +958,6 @@
         "system/bt/gd",
     ],
     generated_headers: [
-        "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
@@ -1017,7 +1017,6 @@
         "system/bt/utils/include",
     ],
     generated_headers: [
-        "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
diff --git a/stack/a2dp/a2dp_vendor_ldac_decoder.cc b/stack/a2dp/a2dp_vendor_ldac_decoder.cc
index c4ea2a6..58cbe28 100644
--- a/stack/a2dp/a2dp_vendor_ldac_decoder.cc
+++ b/stack/a2dp/a2dp_vendor_ldac_decoder.cc
@@ -218,10 +218,8 @@
     return false;
   }
 
-  LDACBT_SMPL_FMT_T fmt;
   int bs_bytes, frame_number;
 
-  fmt = LDACBT_SMPL_FMT_S32;
   frame_number = (int)pBuffer[0];
   bs_bytes = (int)bytesValid;
   bytesValid -= 1;
diff --git a/stack/btm/btm_ble.cc b/stack/btm/btm_ble.cc
index e249631..805a1e2 100644
--- a/stack/btm/btm_ble.cc
+++ b/stack/btm/btm_ble.cc
@@ -485,8 +485,7 @@
       p_dev_rec->device_type = p_inq_info->results.device_type;
       p_dev_rec->ble.ble_addr_type = p_inq_info->results.ble_addr_type;
     }
-    if (p_dev_rec->bd_addr == remote_bda &&
-        p_dev_rec->ble.pseudo_addr == remote_bda) {
+    if (p_dev_rec->bd_addr == remote_bda && p_dev_rec->device_type != 0) {
       *p_dev_type = p_dev_rec->device_type;
       *p_addr_type = p_dev_rec->ble.ble_addr_type;
     } else if (p_dev_rec->ble.pseudo_addr == remote_bda) {
@@ -494,6 +493,7 @@
       *p_addr_type = p_dev_rec->ble.ble_addr_type;
     } else /* matching static adddress only */
     {
+      LOG(ERROR) << __func__ << " device_type not set; assuming BR/EDR";
       *p_dev_type = BT_DEVICE_TYPE_BREDR;
       *p_addr_type = BLE_ADDR_PUBLIC;
     }
diff --git a/stack/btm/btm_ble_adv_filter.cc b/stack/btm/btm_ble_adv_filter.cc
index a2290bf..7e3b45d 100644
--- a/stack/btm/btm_ble_adv_filter.cc
+++ b/stack/btm/btm_ble_adv_filter.cc
@@ -479,12 +479,13 @@
   UINT8_TO_STREAM(p, filt_index);
 
   if (action != BTM_BLE_SCAN_COND_CLEAR) {
-    if (addr.type == BLE_ADDR_PUBLIC_ID) {
+    if (addr.type == 2 /* DEVICE_TYPE_ALL in ScanFilterQueue.java */) {
       LOG(INFO) << __func__ << " Filter address " << addr.bda
-                << " has type PUBLIC_ID, try to get identity address";
+                << " has DEVICE_TYPE_ALL, try to get identity address";
       /* If no matching identity address is found for the input address,
        * this call will have no effect. */
-      btm_random_pseudo_to_identity_addr(&addr.bda, &addr.type);
+      uint8_t dummy_addr_type;
+      btm_random_pseudo_to_identity_addr(&addr.bda, &dummy_addr_type);
     }
 
     LOG(INFO) << __func__
diff --git a/stack/btm/btm_ble_privacy.cc b/stack/btm/btm_ble_privacy.cc
index d3a2b96..4b8ffd0 100644
--- a/stack/btm/btm_ble_privacy.cc
+++ b/stack/btm/btm_ble_privacy.cc
@@ -280,6 +280,7 @@
   }
 
   if (status == HCI_SUCCESS) {
+    btm_ble_update_resolving_list(pseudo_bda, true);
     /* privacy 1.2 command complete does not have these extra byte */
     if (evt_len > 2) {
       /* VSC complete has one extra byte for op code, skip it here */
@@ -740,7 +741,6 @@
     return false;
   }
 
-  btm_ble_update_resolving_list(p_dev_rec->bd_addr, true);
   if (controller_get_interface()->supports_ble_privacy()) {
     const Octet16& peer_irk = p_dev_rec->ble.keys.irk;
     const Octet16& local_irk = btm_cb.devcb.id_keys.irk;
diff --git a/stack/btm/btm_devctl.cc b/stack/btm/btm_devctl.cc
index a3bf3c6..9d5568a 100644
--- a/stack/btm/btm_devctl.cc
+++ b/stack/btm/btm_devctl.cc
@@ -38,6 +38,7 @@
 #include "hci/include/hci_packet_factory.h"
 #include "main/shim/btm_api.h"
 #include "main/shim/controller.h"
+#include "main/shim/entry.h"
 #include "main/shim/hci_layer.h"
 #include "main/shim/shim.h"
 #include "osi/include/compat.h"
@@ -663,10 +664,7 @@
   }
 
   /* mask off all of event from controller */
-  bluetooth::shim::hci_layer_get_interface()->transmit_command(
-      hci_packet_factory_get_interface()->make_set_event_mask(
-          (const bt_event_mask_t*)("\x00\x00\x00\x00\x00\x00\x00\x00")),
-      NULL, NULL, NULL);
+  bluetooth::shim::controller_clear_event_mask();
 
   /* Send the HCI command */
   btsnd_hcic_enable_test_mode();
diff --git a/stack/btm/btm_int_types.h b/stack/btm/btm_int_types.h
index 8b96a42..9800310 100644
--- a/stack/btm/btm_int_types.h
+++ b/stack/btm/btm_int_types.h
@@ -275,6 +275,7 @@
   uint8_t pairing_flags{0};         /* The current pairing flags    */
   RawAddress pairing_bda;           /* The device currently pairing */
   alarm_t* pairing_timer{nullptr};  /* Timer for pairing process    */
+  alarm_t* execution_wait_timer{nullptr}; /* To avoid concurrent auth request */
   uint16_t disc_handle{0};          /* for legacy devices */
   uint8_t disc_reason{0};           /* for legacy devices */
   tBTM_SEC_SERV_REC sec_serv_rec[BTM_SEC_MAX_SERVICE_RECORDS];
@@ -332,6 +333,7 @@
     sec_pending_q = fixed_queue_new(SIZE_MAX);
     sec_collision_timer = alarm_new("btm.sec_collision_timer");
     pairing_timer = alarm_new("btm.pairing_timer");
+    execution_wait_timer = alarm_new("btm.execution_wait_timer");
 
 #if defined(BTM_INITIAL_TRACE_LEVEL)
     trace_level = BTM_INITIAL_TRACE_LEVEL;
@@ -373,6 +375,9 @@
 
     alarm_free(pairing_timer);
     pairing_timer = nullptr;
+
+    alarm_free(execution_wait_timer);
+    execution_wait_timer = nullptr;
   }
 
  private:
diff --git a/stack/btm/btm_sco.cc b/stack/btm/btm_sco.cc
index b80bd40..2d1e1ea 100644
--- a/stack/btm/btm_sco.cc
+++ b/stack/btm/btm_sco.cc
@@ -198,7 +198,8 @@
     return;
   }
   LOG_INFO("Received SCO packet from HCI. Dropping it since no handler so far");
-  uint16_t handle = handle_with_flags & 0xeff;
+  uint16_t handle = handle_with_flags & 0xFFF;
+  ASSERT_LOG(handle <= 0xEFF, "Require handle <= 0xEFF, but is 0x%X", handle);
   auto* active_sco = btm_get_active_sco();
   if (active_sco != nullptr && active_sco->hci_handle == handle) {
     // TODO: For MSBC, we need to decode here
diff --git a/stack/btm/btm_sec.cc b/stack/btm/btm_sec.cc
index 480b4f3..e0fb60e 100644
--- a/stack/btm/btm_sec.cc
+++ b/stack/btm/btm_sec.cc
@@ -89,7 +89,8 @@
 tBTM_SEC_SERV_REC* btm_sec_find_first_serv(bool is_originator, uint16_t psm);
 
 static bool btm_sec_start_get_name(tBTM_SEC_DEV_REC* p_dev_rec);
-static void btm_sec_start_authentication(tBTM_SEC_DEV_REC* p_dev_rec);
+static void btm_sec_wait_and_start_authentication(tBTM_SEC_DEV_REC* p_dev_rec);
+static void btm_sec_auth_timer_timeout(void* data);
 static void btm_sec_collision_timeout(void* data);
 static void btm_restore_mode(void);
 static void btm_sec_pairing_timeout(void* data);
@@ -808,7 +809,7 @@
 
   /* If connection already exists... */
   if (BTM_IsAclConnectionUpAndHandleValid(bd_addr, transport)) {
-    btm_sec_start_authentication(p_dev_rec);
+    btm_sec_wait_and_start_authentication(p_dev_rec);
 
     btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ);
 
@@ -3320,6 +3321,11 @@
                       __func__, p_dev_rec, p_dev_rec->p_callback);
       p_dev_rec->p_callback = NULL;
       l2cu_resubmit_pending_sec_req(&p_dev_rec->bd_addr);
+#ifdef BTM_DISABLE_CONCURRENT_PEER_AUTH
+    } else if (BTM_DISABLE_CONCURRENT_PEER_AUTH &&
+               p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) {
+      p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
+#endif
     }
     return;
   }
@@ -3923,6 +3929,10 @@
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_or_alloc_dev(bda);
 
   VLOG(2) << __func__ << " bda: " << bda;
+#if (defined(BTM_DISABLE_CONCURRENT_PEER_AUTH) && \
+     (BTM_DISABLE_CONCURRENT_PEER_AUTH == TRUE))
+  p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
+#endif
 
   if ((btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) &&
       (btm_cb.collision_start_time != 0) &&
@@ -4315,7 +4325,7 @@
             BTM_SEC_AUTHENTICATED);
     }
 
-    btm_sec_start_authentication(p_dev_rec);
+    btm_sec_wait_and_start_authentication(p_dev_rec);
     return (BTM_CMD_STARTED);
   } else {
     LOG_DEBUG("Authentication not required");
@@ -4378,12 +4388,30 @@
 
 /*******************************************************************************
  *
- * Function         btm_sec_start_authentication
+ * Function         btm_sec_wait_and_start_authentication
  *
- * Description      This function is called to start authentication
+ * Description      This function is called to add an alarm to wait and start
+ *                  authentication
  *
  ******************************************************************************/
-static void btm_sec_start_authentication(tBTM_SEC_DEV_REC* p_dev_rec) {
+static void btm_sec_wait_and_start_authentication(tBTM_SEC_DEV_REC* p_dev_rec) {
+  if (alarm_is_scheduled(btm_cb.execution_wait_timer)) {
+    BTM_TRACE_EVENT("%s: alarm already scheduled", __func__);
+    return;
+  }
+  alarm_set(btm_cb.execution_wait_timer, BTM_DELAY_AUTH_MS,
+            btm_sec_auth_timer_timeout, p_dev_rec);
+}
+
+/*******************************************************************************
+ *
+ * Function         btm_sec_auth_timer_timeout
+ *
+ * Description      called after wait timeout to request authentication
+ *
+ ******************************************************************************/
+static void btm_sec_auth_timer_timeout(void* data) {
+  tBTM_SEC_DEV_REC* p_dev_rec = (tBTM_SEC_DEV_REC*)data;
   p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
   btsnd_hcic_auth_request(p_dev_rec->hci_handle);
 }
diff --git a/stack/gatt/gatt_int.h b/stack/gatt/gatt_int.h
index 8cb51b6..5349284 100644
--- a/stack/gatt/gatt_int.h
+++ b/stack/gatt/gatt_int.h
@@ -262,6 +262,14 @@
 #define GATT_GAP_START_HANDLE 20
 #define GATT_APP_START_HANDLE 40
 
+#ifndef GATT_DEFAULT_START_HANDLE
+#define GATT_DEFAULT_START_HANDLE GATT_GATT_START_HANDLE
+#endif
+
+#ifndef GATT_LAST_HANDLE
+#define GATT_LAST_HANDLE 0xFFFF
+#endif
+
 typedef struct hdl_cfg {
   uint16_t gatt_start_hdl;
   uint16_t gap_start_hdl;
diff --git a/stack/gatt/gatt_main.cc b/stack/gatt/gatt_main.cc
index caec1fb..605aa45 100644
--- a/stack/gatt/gatt_main.cc
+++ b/stack/gatt/gatt_main.cc
@@ -867,8 +867,8 @@
 
   uint8_t handle_range[GATT_SIZE_OF_SRV_CHG_HNDL_RANGE];
   uint8_t* p = handle_range;
-  UINT16_TO_STREAM(p, 1);
-  UINT16_TO_STREAM(p, 0xFFFF);
+  UINT16_TO_STREAM(p, GATT_DEFAULT_START_HANDLE);
+  UINT16_TO_STREAM(p, GATT_LAST_HANDLE);
   GATTS_HandleValueIndication(conn_id, gatt_cb.handle_of_h_r,
                               GATT_SIZE_OF_SRV_CHG_HNDL_RANGE, handle_range);
 }
diff --git a/stack/include/pan_api.h b/stack/include/pan_api.h
index 4d038c5..a9f3b16 100644
--- a/stack/include/pan_api.h
+++ b/stack/include/pan_api.h
@@ -24,6 +24,8 @@
 #ifndef PAN_API_H
 #define PAN_API_H
 
+#include <base/strings/stringprintf.h>
+
 #include <cstdint>
 
 #include "bnep_api.h"
@@ -49,19 +51,23 @@
 
 /* Bit map for PAN roles */
 #define PAN_ROLE_CLIENT 0x01     /* PANU role */
+#define PAN_ROLE_GROUP 0x02      /* Adhoc network group role */
 #define PAN_ROLE_NAP_SERVER 0x04 /* NAP role */
 typedef uint8_t tPAN_ROLE;
 
-/* Bitmap to indicate the usage of the Data */
-#define PAN_DATA_TO_HOST 0x01
-#define PAN_DATA_TO_LAN 0x02
+inline const std::string pan_role_to_text(const tPAN_ROLE& role) {
+  return base::StringPrintf("%c%c%c[0x%x]",
+                            (role & PAN_ROLE_CLIENT) ? 'C' : '.',
+                            (role & PAN_ROLE_GROUP) ? 'G' : '.',
+                            (role & PAN_ROLE_NAP_SERVER) ? 'N' : '.', role);
+}
 
 /*****************************************************************************
  *  Type Definitions
  ****************************************************************************/
 
 /* Define the result codes from PAN */
-enum {
+typedef enum : uint8_t {
   PAN_SUCCESS, /* Success                           */
   PAN_DISCONNECTED = BNEP_CONN_DISCONNECTED, /* Connection terminated   */
   PAN_CONN_FAILED = BNEP_CONN_FAILED,   /* Connection failed                 */
@@ -87,10 +93,43 @@
   PAN_IGNORE_CMD = BNEP_IGNORE_CMD,       /* To ignore the rcvd command */
   PAN_TX_FLOW_ON = BNEP_TX_FLOW_ON,       /* tx data flow enabled */
   PAN_TX_FLOW_OFF = BNEP_TX_FLOW_OFF,     /* tx data flow disabled */
-  PAN_FAILURE                             /* Failure                      */
+  PAN_FAILURE = 19,                       /* Failure                      */
+  PAN_HOTSPOT_DISABLED = 20,              /* Hotspot disabled             */
+} tPAN_RESULT;
 
-};
-typedef uint8_t tPAN_RESULT;
+#define CASE_RETURN_TEXT(code) \
+  case code:                   \
+    return #code
+
+inline const std::string pan_result_text(const tPAN_RESULT& result) {
+  switch (result) {
+    CASE_RETURN_TEXT(PAN_SUCCESS);
+    CASE_RETURN_TEXT(PAN_DISCONNECTED);
+    CASE_RETURN_TEXT(PAN_CONN_FAILED);
+    CASE_RETURN_TEXT(PAN_NO_RESOURCES);
+    CASE_RETURN_TEXT(PAN_MTU_EXCEDED);
+    CASE_RETURN_TEXT(PAN_INVALID_OFFSET);
+    CASE_RETURN_TEXT(PAN_CONN_FAILED_CFG);
+    CASE_RETURN_TEXT(PAN_INVALID_SRC_ROLE);
+    CASE_RETURN_TEXT(PAN_INVALID_DST_ROLE);
+    CASE_RETURN_TEXT(PAN_CONN_FAILED_UUID_SIZE);
+    CASE_RETURN_TEXT(PAN_Q_SIZE_EXCEEDED);
+    CASE_RETURN_TEXT(PAN_TOO_MANY_FILTERS);
+    CASE_RETURN_TEXT(PAN_SET_FILTER_FAIL);
+    CASE_RETURN_TEXT(PAN_WRONG_HANDLE);
+    CASE_RETURN_TEXT(PAN_WRONG_STATE);
+    CASE_RETURN_TEXT(PAN_SECURITY_FAIL);
+    CASE_RETURN_TEXT(PAN_IGNORE_CMD);
+    CASE_RETURN_TEXT(PAN_TX_FLOW_ON);
+    CASE_RETURN_TEXT(PAN_TX_FLOW_OFF);
+    CASE_RETURN_TEXT(PAN_FAILURE);
+    CASE_RETURN_TEXT(PAN_HOTSPOT_DISABLED);
+    default:
+      return base::StringPrintf("UNKNOWN[%hhu]", result);
+  }
+}
+
+#undef CASE_RETURN_TEXT
 
 /*****************************************************************
  *       Callback Function Prototypes
diff --git a/stack/l2cap/l2c_csm.cc b/stack/l2cap/l2c_csm.cc
index ded9eab..29229d1 100644
--- a/stack/l2cap/l2c_csm.cc
+++ b/stack/l2cap/l2c_csm.cc
@@ -505,6 +505,12 @@
         ** stack version   : 05.04.11.20060119
         */
 
+        /* Cancel ccb timer as security complete. waiting for w4_info_rsp
+        ** once info rsp received, connection rsp timer will be started
+        ** while sending connection ind to profiles
+        */
+        alarm_cancel(p_ccb->l2c_ccb_timer);
+
         /* Waiting for the info resp, tell the peer to set a longer timer */
         LOG_DEBUG("Waiting for info response, sending connect pending");
         l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0);
diff --git a/stack/pan/pan_api.cc b/stack/pan/pan_api.cc
index bf3c387..62baf2e 100644
--- a/stack/pan/pan_api.cc
+++ b/stack/pan/pan_api.cc
@@ -145,7 +145,6 @@
 
   /* Register all the roles with SDP */
   PAN_TRACE_API("PAN_SetRole() called with role 0x%x", role);
-#if (PAN_SUPPORTS_ROLE_NAP == TRUE)
   if (role & PAN_ROLE_NAP_SERVER) {
     /* Check the service name */
     if ((p_nap_name == NULL) || (*p_nap_name == 0))
@@ -170,9 +169,7 @@
       bta_sys_remove_uuid(UUID_SERVCLASS_NAP);
     }
   }
-#endif
 
-#if (PAN_SUPPORTS_ROLE_PANU == TRUE)
   if (role & PAN_ROLE_CLIENT) {
     /* Check the service name */
     if ((p_user_name == NULL) || (*p_user_name == 0))
@@ -196,7 +193,6 @@
       bta_sys_remove_uuid(UUID_SERVCLASS_PANU);
     }
   }
-#endif
 
   pan_cb.role = role;
   PAN_TRACE_EVENT("PAN role set to: %d", role);
diff --git a/stack/pan/pan_int.h b/stack/pan/pan_int.h
index 0b49ba9..fec0786 100644
--- a/stack/pan/pan_int.h
+++ b/stack/pan/pan_int.h
@@ -40,13 +40,16 @@
 
 #define PAN_PROFILE_VERSION 0x0100 /* Version 1.00 */
 
+typedef enum : uint8_t {
+  PAN_STATE_IDLE = 0,
+  PAN_STATE_CONN_START = 1,
+  PAN_STATE_CONNECTED = 2,
+} tPAN_STATE;
+
 /* Define the PAN Connection Control Block
  */
 typedef struct {
-#define PAN_STATE_IDLE 0
-#define PAN_STATE_CONN_START 1
-#define PAN_STATE_CONNECTED 2
-  uint8_t con_state;
+  tPAN_STATE con_state;
 
 #define PAN_FLAGS_CONN_COMPLETED 0x01
   uint8_t con_flags;
diff --git a/stack/pan/pan_utils.cc b/stack/pan/pan_utils.cc
index 4505e37..7252ddf 100644
--- a/stack/pan/pan_utils.cc
+++ b/stack/pan/pan_utils.cc
@@ -104,7 +104,6 @@
   SDP_AddAttribute(sdp_handle, ATTR_ID_SECURITY_DESCRIPTION, UINT_DESC_TYPE, 2,
                    (uint8_t*)&security);
 
-#if (PAN_SUPPORTS_ROLE_NAP == TRUE)
   if (uuid == UUID_SERVCLASS_NAP) {
     uint16_t NetAccessType = 0x0005;      /* Ethernet */
     uint32_t NetAccessRate = 0x0001312D0; /* 10Mb/sec */
@@ -122,7 +121,6 @@
     SDP_AddAttribute(sdp_handle, ATTR_ID_MAX_NET_ACCESS_RATE, UINT_DESC_TYPE, 4,
                      array);
   }
-#endif
 
   /* Make the service browsable */
   SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse_list);
diff --git a/stack/test/btm/stack_btm_test.cc b/stack/test/btm/stack_btm_test.cc
index 3fe61bc..68a741b 100644
--- a/stack/test/btm/stack_btm_test.cc
+++ b/stack/test/btm/stack_btm_test.cc
@@ -48,9 +48,6 @@
 btif_hh_cb_t btif_hh_cb;
 tL2C_CB l2cb;
 
-const hci_packet_factory_t* hci_packet_factory_get_interface() {
-  return nullptr;
-}
 const hci_t* hci_layer_get_interface() { return nullptr; }
 
 void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
diff --git a/stack/test/fuzzers/Android.bp b/stack/test/fuzzers/Android.bp
index 5d51ad6..d22e155 100644
--- a/stack/test/fuzzers/Android.bp
+++ b/stack/test/fuzzers/Android.bp
@@ -38,6 +38,7 @@
         "libosi",
         "libudrv-uipc",
         "libbt-protos-lite",
+        "libbluetooth-dumpsys",
         "libbluetooth_gd",
     ],
     shared_libs: [
diff --git a/test/headless/Android.bp b/test/headless/Android.bp
index 65c74bd..c1b3052 100644
--- a/test/headless/Android.bp
+++ b/test/headless/Android.bp
@@ -37,6 +37,7 @@
         "libbluetooth_gd",
         "libbt-bta",
         "libbt-common",
+        "libbluetooth-dumpsys",
         "libbt-hci",
         "libbt-protos-lite",
         "libbt-sbc-decoder",
diff --git a/test/mock/mock_hci_packet_factory.cc b/test/mock/mock_hci_packet_factory.cc
index a80f17c..e69de29 100644
--- a/test/mock/mock_hci_packet_factory.cc
+++ b/test/mock/mock_hci_packet_factory.cc
@@ -1,36 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Generated mock file from original source file
- *   Functions generated:2
- */
-
-#include <map>
-#include <string>
-
-extern std::map<std::string, int> mock_function_count_map;
-
-#include "hci/include/hci_packet_factory.h"
-
-#ifndef UNUSED_ATTR
-#define UNUSED_ATTR
-#endif
-
-const hci_packet_factory_t* hci_packet_factory_get_interface() {
-  mock_function_count_map[__func__]++;
-  return nullptr;
-}
diff --git a/test/mock/mock_main_shim_controller.cc b/test/mock/mock_main_shim_controller.cc
index d3ff4b8..f83d5a2 100644
--- a/test/mock/mock_main_shim_controller.cc
+++ b/test/mock/mock_main_shim_controller.cc
@@ -35,3 +35,7 @@
   mock_function_count_map[__func__]++;
   return nullptr;
 }
+
+void bluetooth::shim::controller_clear_event_mask() {
+  mock_function_count_map[__func__]++;
+}
diff --git a/test/rootcanal/Android.bp b/test/rootcanal/Android.bp
index 8c5b4b7..d611cab 100644
--- a/test/rootcanal/Android.bp
+++ b/test/rootcanal/Android.bp
@@ -72,6 +72,7 @@
         "system/bt/stack/include",
     ],
     init_rc: ["android.hardware.bluetooth@1.1-service.sim.rc"],
+    required: ["bt_controller_properties"],
 }
 
 cc_library_shared {
diff --git a/vendor_libs/test_vendor_lib/Android.bp b/vendor_libs/test_vendor_lib/Android.bp
index c4174da..62d0392 100644
--- a/vendor_libs/test_vendor_lib/Android.bp
+++ b/vendor_libs/test_vendor_lib/Android.bp
@@ -53,6 +53,7 @@
         "net/posix/posix_async_socket_server.cc",
         ":BluetoothPacketSources",
         ":BluetoothHciClassSources",
+        ":BluetoothCryptoToolboxSources",
     ],
     cflags: [
         "-Wall",
diff --git a/vendor_libs/test_vendor_lib/data/Android.bp b/vendor_libs/test_vendor_lib/data/Android.bp
index ef33f4a..7a634af 100644
--- a/vendor_libs/test_vendor_lib/data/Android.bp
+++ b/vendor_libs/test_vendor_lib/data/Android.bp
@@ -12,3 +12,11 @@
     src: "controller_properties.json",
     sub_dir: "rootcanal/data",
 }
+
+prebuilt_etc {
+    name: "bt_controller_properties",
+    src: "controller_properties.json",
+    vendor: true,
+    filename_from_src: true,
+    sub_dir: "bluetooth",
+}
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 830bebf..654aeac 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
@@ -226,6 +226,10 @@
   SET_SUPPORTED(LE_RAND, LeRand);
   SET_SUPPORTED(LE_READ_SUPPORTED_STATES, LeReadSupportedStates);
   SET_HANDLER(LE_GET_VENDOR_CAPABILITIES, LeVendorCap);
+  SET_HANDLER(LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY,
+              LeRemoteConnectionParameterRequestReply);
+  SET_HANDLER(LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY,
+              LeRemoteConnectionParameterRequestNegativeReply);
   SET_HANDLER(LE_MULTI_ADVT, LeVendorMultiAdv);
   SET_HANDLER(LE_ADV_FILTER, LeAdvertisingFilter);
   SET_HANDLER(LE_ENERGY_INFO, LeEnergyInfo);
@@ -1513,7 +1517,7 @@
   auto key = command_view.GetLinkKey();
   auto status = link_layer_controller_.LinkKeyRequestReply(addr, key);
   auto packet = bluetooth::hci::LinkKeyRequestReplyCompleteBuilder::Create(
-      kNumCommandPackets, status);
+      kNumCommandPackets, status, addr);
   send_event_(std::move(packet));
 }
 
@@ -1794,11 +1798,13 @@
       gd_hci::LeConnectionManagementCommandView::Create(
           gd_hci::AclCommandView::Create(command)));
   ASSERT(command_view.IsValid());
-  ErrorCode status = link_layer_controller_.LeConnectionUpdate(command_view);
+  ErrorCode status = link_layer_controller_.LeConnectionUpdate(
+      command_view.GetConnectionHandle(), command_view.GetConnIntervalMin(),
+      command_view.GetConnIntervalMax(), command_view.GetConnLatency(),
+      command_view.GetSupervisionTimeout());
 
-  auto status_packet = bluetooth::hci::LeConnectionUpdateStatusBuilder::Create(
-      status, kNumCommandPackets);
-  send_event_(std::move(status_packet));
+  send_event_(bluetooth::hci::LeConnectionUpdateStatusBuilder::Create(
+      status, kNumCommandPackets));
 }
 
 void DualModeController::CreateConnection(CommandView command) {
@@ -1998,16 +2004,13 @@
       gd_hci::LeSecurityCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
-  uint8_t addr_type =
+  auto addr_type =
       static_cast<uint8_t>(command_view.GetPeerIdentityAddressType());
   Address address = command_view.GetPeerIdentityAddress();
-  std::array<uint8_t, LinkLayerController::kIrk_size> peerIrk =
-      command_view.GetPeerIrk();
-  std::array<uint8_t, LinkLayerController::kIrk_size> localIrk =
-      command_view.GetLocalIrk();
 
   auto status = link_layer_controller_.LeResolvingListAddDevice(
-      address, addr_type, peerIrk, localIrk);
+      address, addr_type, command_view.GetPeerIrk(),
+      command_view.GetLocalIrk());
   auto packet =
       bluetooth::hci::LeAddDeviceToResolvingListCompleteBuilder::Create(
           kNumCommandPackets, status);
@@ -2323,6 +2326,39 @@
   send_event_(std::move(packet));
 }
 
+void DualModeController::LeRemoteConnectionParameterRequestReply(
+    CommandView command) {
+  auto command_view =
+      gd_hci::LeRemoteConnectionParameterRequestReplyView::Create(
+          gd_hci::LeConnectionManagementCommandView::Create(
+              gd_hci::AclCommandView::Create(command)));
+  ASSERT(command_view.IsValid());
+  auto status = link_layer_controller_.LeRemoteConnectionParameterRequestReply(
+      command_view.GetConnectionHandle(), command_view.GetIntervalMin(),
+      command_view.GetIntervalMax(), command_view.GetTimeout(),
+      command_view.GetLatency(), command_view.GetMinimumCeLength(),
+      command_view.GetMaximumCeLength());
+  send_event_(
+      gd_hci::LeRemoteConnectionParameterRequestReplyCompleteBuilder::Create(
+          kNumCommandPackets, status, command_view.GetConnectionHandle()));
+}
+
+void DualModeController::LeRemoteConnectionParameterRequestNegativeReply(
+    CommandView command) {
+  auto command_view =
+      gd_hci::LeRemoteConnectionParameterRequestNegativeReplyView::Create(
+          gd_hci::LeConnectionManagementCommandView::Create(
+              gd_hci::AclCommandView::Create(command)));
+  ASSERT(command_view.IsValid());
+  auto status =
+      link_layer_controller_.LeRemoteConnectionParameterRequestNegativeReply(
+          command_view.GetConnectionHandle(), command_view.GetReason());
+  send_event_(
+      gd_hci::LeRemoteConnectionParameterRequestNegativeReplyCompleteBuilder::
+          Create(kNumCommandPackets, status,
+                 command_view.GetConnectionHandle()));
+}
+
 void DualModeController::LeVendorCap(CommandView command) {
   auto command_view = gd_hci::LeGetVendorCapabilitiesView::Create(
       gd_hci::VendorCommandView::Create(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 5135b76..9c5bc93 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
@@ -48,7 +48,8 @@
 // "Hci" to distinguish it as a controller command.
 class DualModeController : public Device {
   // The location of the config file loaded to populate controller attributes.
-  static constexpr char kControllerPropertiesFile[] = "/etc/bluetooth/controller_properties.json";
+  static constexpr char kControllerPropertiesFile[] =
+      "/vendor/etc/bluetooth/controller_properties.json";
   static constexpr uint16_t kSecurityManagerNumKeys = 15;
 
  public:
@@ -465,6 +466,12 @@
   // 7.8.27
   void LeReadSupportedStates(CommandView args);
 
+  // 7.8.31
+  void LeRemoteConnectionParameterRequestReply(CommandView args);
+
+  // 7.8.32
+  void LeRemoteConnectionParameterRequestNegativeReply(CommandView args);
+
   // 7.8.34
   void LeReadSuggestedDefaultDataLength(CommandView 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
index 841806d..cbb4cb7 100644
--- a/vendor_libs/test_vendor_lib/model/controller/le_advertiser.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/le_advertiser.cc
@@ -79,6 +79,7 @@
 void LeAdvertiser::Enable() {
   enabled_ = true;
   last_le_advertisement_ = std::chrono::steady_clock::now() - interval_;
+  num_events_ = 0;
   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());
@@ -86,11 +87,11 @@
 
 void LeAdvertiser::EnableExtended(
     std::chrono::steady_clock::duration duration) {
-  last_le_advertisement_ = std::chrono::steady_clock::now();
+  Enable();
   if (duration != std::chrono::milliseconds(0)) {
     ending_time_ = std::chrono::steady_clock::now() + duration;
   }
-  enabled_ = true;
+  extended_ = 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());
@@ -100,6 +101,10 @@
 
 bool LeAdvertiser::IsEnabled() const { return enabled_; }
 
+bool LeAdvertiser::IsExtended() const { return extended_; }
+
+uint8_t LeAdvertiser::GetNumAdvertisingEvents() const { return num_events_; }
+
 std::unique_ptr<model::packets::LeAdvertisementBuilder>
 LeAdvertiser::GetAdvertisement(std::chrono::steady_clock::time_point now) {
   if (!enabled_) {
@@ -116,6 +121,7 @@
   }
 
   last_le_advertisement_ = now;
+  num_events_ += (num_events_ < 255 ? 1 : 0);
   return model::packets::LeAdvertisementBuilder::Create(
       address_.GetAddress(), peer_address_.GetAddress(),
       static_cast<model::packets::AddressType>(address_.GetAddressType()),
diff --git a/vendor_libs/test_vendor_lib/model/controller/le_advertiser.h b/vendor_libs/test_vendor_lib/model/controller/le_advertiser.h
index 109b47d..f591c25 100644
--- a/vendor_libs/test_vendor_lib/model/controller/le_advertiser.h
+++ b/vendor_libs/test_vendor_lib/model/controller/le_advertiser.h
@@ -69,6 +69,10 @@
 
   bool IsEnabled() const;
 
+  bool IsExtended() const;
+
+  uint8_t GetNumAdvertisingEvents() const;
+
   bluetooth::hci::AddressWithType GetAddress() const;
 
  private:
@@ -81,6 +85,8 @@
   std::vector<uint8_t> scan_response_;
   std::chrono::steady_clock::duration interval_{};
   std::chrono::steady_clock::time_point ending_time_{};
+  uint8_t num_events_{0};
+  bool extended_{false};
   bool enabled_{false};
   std::chrono::steady_clock::time_point last_le_advertisement_;
 };
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 774c9ae..dc1ffbb 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
@@ -18,6 +18,7 @@
 
 #include <hci/hci_packets.h>
 
+#include "crypto_toolbox/crypto_toolbox.h"
 #include "include/le_advertisement.h"
 #include "os/log.h"
 #include "packet/raw_builder.h"
@@ -286,6 +287,12 @@
     case model::packets::PacketType::LE_CONNECT_COMPLETE:
       IncomingLeConnectCompletePacket(incoming);
       break;
+    case model::packets::PacketType::LE_CONNECTION_PARAMETER_REQUEST:
+      IncomingLeConnectionParameterRequest(incoming);
+      break;
+    case model::packets::PacketType::LE_CONNECTION_PARAMETER_UPDATE:
+      IncomingLeConnectionParameterUpdate(incoming);
+      break;
     case model::packets::PacketType::LE_ENCRYPT_CONNECTION:
       IncomingLeEncryptConnection(incoming);
       break;
@@ -1143,6 +1150,19 @@
   }
 }
 
+static bool rpa_matches_irk(
+    Address rpa, std::array<uint8_t, LinkLayerController::kIrkSize> irk) {
+  // 1.3.2.3 Private device address resolution
+  uint8_t hash[3] = {rpa.address[0], rpa.address[1], rpa.address[2]};
+  uint8_t prand[3] = {rpa.address[3], rpa.address[4], rpa.address[5]};
+
+  // generate X = E irk(R0, R1, R2) and R is random address 3 LSO
+  auto x = bluetooth::crypto_toolbox::aes_128(irk, &prand[0], 3);
+
+  // If the hashes match, this is the IRK
+  return (memcmp(x.data(), &hash[0], 3) == 0);
+}
+
 void LinkLayerController::IncomingLeAdvertisementPacket(
     model::packets::LinkLayerPacketView incoming) {
   // TODO: Handle multiple advertisements per packet.
@@ -1226,13 +1246,27 @@
     SendLeLinkLayerPacket(std::move(to_send));
   }
 
+  Address resolved_address = address;
+  uint8_t resolved_address_type = static_cast<uint8_t>(address_type);
+  bool resolved = false;
+  for (const auto& entry : le_resolving_list_) {
+    if (rpa_matches_irk(address, entry.peer_irk)) {
+      resolved = true;
+      resolved_address = entry.address;
+      resolved_address_type = entry.address_type;
+    }
+  }
+
   // Connect
   if ((le_connect_ && le_peer_address_ == address &&
        le_peer_address_type_ == 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)))) {
+                                   static_cast<uint8_t>(address_type))) ||
+      (resolved &&
+       LeConnectListContainsDevice(
+           resolved_address, static_cast<uint8_t>(resolved_address_type)))) {
     if (!connections_.CreatePendingLeConnection(AddressWithType(
             address, static_cast<bluetooth::hci::AddressType>(address_type)))) {
       LOG_WARN(
@@ -1270,27 +1304,28 @@
   }
 }
 
-void LinkLayerController::HandleLeConnection(AddressWithType address,
-                                             AddressWithType own_address,
-                                             uint8_t role,
-                                             uint16_t connection_interval,
-                                             uint16_t connection_latency,
-                                             uint16_t supervision_timeout) {
+uint16_t LinkLayerController::HandleLeConnection(AddressWithType address,
+                                                 AddressWithType own_address,
+                                                 uint8_t role,
+                                                 uint16_t connection_interval,
+                                                 uint16_t connection_latency,
+                                                 uint16_t supervision_timeout) {
   // TODO: Choose between LeConnectionComplete and LeEnhancedConnectionComplete
   uint16_t handle = connections_.CreateLeConnection(address, own_address);
   if (handle == kReservedHandle) {
     LOG_WARN("No pending connection for connection from %s",
              address.ToString().c_str());
-    return;
+    return kReservedHandle;
   }
-  auto packet = bluetooth::hci::LeConnectionCompleteBuilder::Create(
-      ErrorCode::SUCCESS, handle, static_cast<bluetooth::hci::Role>(role),
-      address.GetAddressType(), address.GetAddress(), connection_interval,
-      connection_latency, supervision_timeout,
-      static_cast<bluetooth::hci::ClockAccuracy>(0x00));
   if (properties_.IsUnmasked(EventCode::LE_META_EVENT)) {
+    auto packet = bluetooth::hci::LeConnectionCompleteBuilder::Create(
+        ErrorCode::SUCCESS, handle, static_cast<bluetooth::hci::Role>(role),
+        address.GetAddressType(), address.GetAddress(), connection_interval,
+        connection_latency, supervision_timeout,
+        static_cast<bluetooth::hci::ClockAccuracy>(0x00));
     send_event_(std::move(packet));
   }
+  return handle;
 }
 
 void LinkLayerController::IncomingLeConnectPacket(
@@ -1312,11 +1347,13 @@
   }
   bluetooth::hci::AddressWithType my_address{};
   bool matched_advertiser = false;
-  for (auto advertiser : advertisers_) {
-    AddressWithType advertiser_address = advertiser.GetAddress();
+  size_t set = 0;
+  for (size_t i = 0; i < advertisers_.size(); i++) {
+    AddressWithType advertiser_address = advertisers_[i].GetAddress();
     if (incoming.GetDestinationAddress() == advertiser_address.GetAddress()) {
       my_address = advertiser_address;
       matched_advertiser = true;
+      set = i;
     }
   }
 
@@ -1326,7 +1363,7 @@
     return;
   }
 
-  HandleLeConnection(
+  uint16_t handle = HandleLeConnection(
       AddressWithType(
           incoming.GetSourceAddress(),
           static_cast<bluetooth::hci::AddressType>(connect.GetAddressType())),
@@ -1340,6 +1377,16 @@
       connect.GetLeConnectionSupervisionTimeout(),
       static_cast<uint8_t>(my_address.GetAddressType()));
   SendLeLinkLayerPacket(std::move(to_send));
+
+  if (advertisers_[set].IsExtended()) {
+    uint8_t num_advertisements = advertisers_[set].GetNumAdvertisingEvents();
+    advertisers_[set].Disable();
+    if (properties_.GetLeEventSupported(
+            bluetooth::hci::SubeventCode::ADVERTISING_SET_TERMINATED)) {
+      send_event_(bluetooth::hci::LeAdvertisingSetTerminatedBuilder::Create(
+          ErrorCode::SUCCESS, set, handle, num_advertisements));
+    }
+  }
 }
 
 void LinkLayerController::IncomingLeConnectCompletePacket(
@@ -1358,6 +1405,51 @@
       complete.GetLeConnectionSupervisionTimeout());
 }
 
+void LinkLayerController::IncomingLeConnectionParameterRequest(
+    model::packets::LinkLayerPacketView incoming) {
+  auto request =
+      model::packets::LeConnectionParameterRequestView::Create(incoming);
+  ASSERT(request.IsValid());
+  Address peer = incoming.GetSourceAddress();
+  uint16_t handle = connections_.GetHandleOnlyAddress(peer);
+  if (handle == kReservedHandle) {
+    LOG_INFO("@%s: Unknown connection @%s",
+             incoming.GetDestinationAddress().ToString().c_str(),
+             peer.ToString().c_str());
+    return;
+  }
+  if (properties_.IsUnmasked(EventCode::LE_META_EVENT) &&
+      properties_.GetLeEventSupported(
+          bluetooth::hci::SubeventCode::CONNECTION_UPDATE_COMPLETE)) {
+    send_event_(
+        bluetooth::hci::LeRemoteConnectionParameterRequestBuilder::Create(
+            handle, request.GetIntervalMin(), request.GetIntervalMax(),
+            request.GetLatency(), request.GetTimeout()));
+  }
+}
+
+void LinkLayerController::IncomingLeConnectionParameterUpdate(
+    model::packets::LinkLayerPacketView incoming) {
+  auto update =
+      model::packets::LeConnectionParameterUpdateView::Create(incoming);
+  ASSERT(update.IsValid());
+  Address peer = incoming.GetSourceAddress();
+  uint16_t handle = connections_.GetHandleOnlyAddress(peer);
+  if (handle == kReservedHandle) {
+    LOG_INFO("@%s: Unknown connection @%s",
+             incoming.GetDestinationAddress().ToString().c_str(),
+             peer.ToString().c_str());
+    return;
+  }
+  if (properties_.IsUnmasked(EventCode::LE_META_EVENT) &&
+      properties_.GetLeEventSupported(
+          bluetooth::hci::SubeventCode::CONNECTION_UPDATE_COMPLETE)) {
+    send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create(
+        static_cast<ErrorCode>(update.GetStatus()), handle,
+        update.GetInterval(), update.GetLatency(), update.GetTimeout()));
+  }
+}
+
 void LinkLayerController::IncomingLeEncryptConnection(
     model::packets::LinkLayerPacketView incoming) {
   LOG_INFO("IncomingLeEncryptConnection");
@@ -2694,16 +2786,12 @@
 }
 
 void LinkLayerController::LeConnectionUpdateComplete(
-    bluetooth::hci::LeConnectionUpdateView connection_update) {
-  uint16_t handle = connection_update.GetConnectionHandle();
+    uint16_t handle, uint16_t interval_min, uint16_t interval_max,
+    uint16_t latency, uint16_t supervision_timeout) {
   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 ||
@@ -2714,27 +2802,73 @@
     status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
   }
   uint16_t interval = (interval_min + interval_max) / 2;
-  if (properties_.IsUnmasked(EventCode::LE_META_EVENT)) {
+
+  SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create(
+      connections_.GetOwnAddress(handle).GetAddress(),
+      connections_.GetAddress(handle).GetAddress(),
+      static_cast<uint8_t>(ErrorCode::SUCCESS), interval, latency,
+      supervision_timeout));
+
+  if (properties_.IsUnmasked(EventCode::LE_META_EVENT) &&
+      properties_.GetLeEventSupported(
+          bluetooth::hci::SubeventCode::CONNECTION_UPDATE_COMPLETE)) {
     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();
+    uint16_t handle, uint16_t interval_min, uint16_t interval_max,
+    uint16_t latency, uint16_t supervision_timeout) {
   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);
-  });
+  SendLeLinkLayerPacket(LeConnectionParameterRequestBuilder::Create(
+      connections_.GetOwnAddress(handle).GetAddress(),
+      connections_.GetAddress(handle).GetAddress(), interval_min, interval_max,
+      latency, supervision_timeout));
 
   return ErrorCode::SUCCESS;
 }
 
+ErrorCode LinkLayerController::LeRemoteConnectionParameterRequestReply(
+    uint16_t connection_handle, uint16_t interval_min, uint16_t interval_max,
+    uint16_t timeout, uint16_t latency, uint16_t minimum_ce_length,
+    uint16_t maximum_ce_length) {
+  if (!connections_.HasHandle(connection_handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+
+  if ((interval_min > interval_max) ||
+      (minimum_ce_length > maximum_ce_length)) {
+    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  }
+
+  ScheduleTask(milliseconds(25), [this, connection_handle, interval_min,
+                                  interval_max, latency, timeout]() {
+    LeConnectionUpdateComplete(connection_handle, interval_min, interval_max,
+                               latency, timeout);
+  });
+  return ErrorCode::SUCCESS;
+}
+
+ErrorCode LinkLayerController::LeRemoteConnectionParameterRequestNegativeReply(
+    uint16_t connection_handle, bluetooth::hci::ErrorCode reason) {
+  if (!connections_.HasHandle(connection_handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+
+  uint16_t interval = 0;
+  uint16_t latency = 0;
+  uint16_t timeout = 0;
+  SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create(
+      connections_.GetOwnAddress(connection_handle).GetAddress(),
+      connections_.GetAddress(connection_handle).GetAddress(),
+      static_cast<uint8_t>(reason), interval, latency, timeout));
+  return ErrorCode::SUCCESS;
+}
+
 ErrorCode LinkLayerController::LeConnectListClear() {
   if (ConnectListBusy()) {
     return ErrorCode::COMMAND_DISALLOWED;
@@ -2772,25 +2906,16 @@
 }
 
 ErrorCode LinkLayerController::LeResolvingListAddDevice(
-    Address addr, uint8_t addr_type, std::array<uint8_t, kIrk_size> peerIrk,
-    std::array<uint8_t, kIrk_size> localIrk) {
+    Address addr, uint8_t addr_type, std::array<uint8_t, kIrkSize> peerIrk,
+    std::array<uint8_t, kIrkSize> localIrk) {
   if (ResolvingListBusy()) {
     return ErrorCode::COMMAND_DISALLOWED;
   }
-  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_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 ErrorCode::SUCCESS;
-    }
-  }
   if (LeResolvingListFull()) {
     return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
   }
-  le_resolving_list_.emplace_back(new_tuple);
+  le_resolving_list_.emplace_back(
+      ResolvingListEntry{addr, addr_type, peerIrk, localIrk});
   return ErrorCode::SUCCESS;
 }
 
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 69edc88..8693add 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
@@ -35,7 +35,7 @@
 
 class LinkLayerController {
  public:
-  static constexpr size_t kIrk_size = 16;
+  static constexpr size_t kIrkSize = 16;
 
   LinkLayerController(const DeviceProperties& properties) : properties_(properties) {}
   ErrorCode SendCommandToRemoteByAddress(
@@ -157,15 +157,22 @@
       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 LeConnectionUpdateComplete(uint16_t handle, uint16_t interval_min,
+                                  uint16_t interval_max, uint16_t latency,
+                                  uint16_t supervision_timeout);
+  ErrorCode LeConnectionUpdate(uint16_t handle, uint16_t interval_min,
+                               uint16_t interval_max, uint16_t latency,
+                               uint16_t supervision_timeout);
+  ErrorCode LeRemoteConnectionParameterRequestReply(
+      uint16_t connection_handle, uint16_t interval_min, uint16_t interval_max,
+      uint16_t timeout, uint16_t latency, uint16_t minimum_ce_length,
+      uint16_t maximum_ce_length);
+  ErrorCode LeRemoteConnectionParameterRequestNegativeReply(
+      uint16_t connection_handle, bluetooth::hci::ErrorCode reason);
+  uint16_t HandleLeConnection(AddressWithType addr, AddressWithType own_addr,
+                              uint8_t role, uint16_t connection_interval,
+                              uint16_t connection_latency,
+                              uint16_t supervision_timeout);
 
   bool ConnectListBusy();
   ErrorCode LeConnectListClear();
@@ -176,8 +183,8 @@
   bool ResolvingListBusy();
   ErrorCode LeResolvingListClear();
   ErrorCode LeResolvingListAddDevice(Address addr, uint8_t addr_type,
-                                     std::array<uint8_t, kIrk_size> peerIrk,
-                                     std::array<uint8_t, kIrk_size> localIrk);
+                                     std::array<uint8_t, kIrkSize> peerIrk,
+                                     std::array<uint8_t, kIrkSize> localIrk);
   ErrorCode LeResolvingListRemoveDevice(Address addr, uint8_t addr_type);
   bool LeResolvingListContainsDevice(Address addr, uint8_t addr_type);
   bool LeResolvingListFull();
@@ -371,6 +378,10 @@
   void IncomingLeConnectPacket(model::packets::LinkLayerPacketView packet);
   void IncomingLeConnectCompletePacket(
       model::packets::LinkLayerPacketView packet);
+  void IncomingLeConnectionParameterRequest(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingLeConnectionParameterUpdate(
+      model::packets::LinkLayerPacketView packet);
   void IncomingLeEncryptConnection(model::packets::LinkLayerPacketView packet);
   void IncomingLeEncryptConnectionResponse(
       model::packets::LinkLayerPacketView packet);
@@ -445,9 +456,13 @@
   std::vector<uint8_t> le_event_mask_;
 
   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_;
+  struct ResolvingListEntry {
+    Address address;
+    uint8_t address_type;
+    std::array<uint8_t, kIrkSize> peer_irk;
+    std::array<uint8_t, kIrkSize> local_irk;
+  };
+  std::vector<ResolvingListEntry> le_resolving_list_;
 
   std::array<LeAdvertiser, 7> advertisers_;
 
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 6c7bcf3..03093e8 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device_properties.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/device_properties.cc
@@ -92,8 +92,8 @@
 
   std::string errs;
   if (!Json::parseFromStream(builder, file, &root, &errs)) {
-    LOG_ERROR("Error reading controller properties from file: %s",
-              errs.c_str());
+    LOG_ERROR("Error reading controller properties from file: %s error: %s",
+              file_name.c_str(), errs.c_str());
     return;
   }
 
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 0bd600d..137d04c 100644
--- a/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
+++ b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
@@ -50,6 +50,8 @@
     PIN_RESPONSE = 0x2B,
     LE_READ_REMOTE_FEATURES = 0x2C,
     LE_READ_REMOTE_FEATURES_RESPONSE = 0x2D,
+    LE_CONNECTION_PARAMETER_REQUEST = 0x2E,
+    LE_CONNECTION_PARAMETER_UPDATE = 0x2F,
 }
 
 packet LinkLayerPacket {
@@ -361,3 +363,17 @@
   features : 64,
   status : 8,
 }
+
+packet LeConnectionParameterRequest : LinkLayerPacket (type = LE_CONNECTION_PARAMETER_REQUEST) {
+  interval_min : 16,
+  interval_max : 16,
+  latency : 16,
+  timeout : 16,
+}
+
+packet LeConnectionParameterUpdate : LinkLayerPacket (type = LE_CONNECTION_PARAMETER_UPDATE) {
+  status : 8,
+  interval : 16,
+  latency : 16,
+  timeout : 16,
+}
diff --git a/vendor_libs/test_vendor_lib/scripts/hci_socket.py b/vendor_libs/test_vendor_lib/scripts/hci_socket.py
index 427b8d4..aed2577 100644
--- a/vendor_libs/test_vendor_lib/scripts/hci_socket.py
+++ b/vendor_libs/test_vendor_lib/scripts/hci_socket.py
@@ -58,7 +58,7 @@
 
 """
 
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import binascii
 import cmd
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 d1268e9..e73ef08 100644
--- a/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py
+++ b/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py
@@ -58,7 +58,7 @@
 
 """
 
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import binascii
 import cmd
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 72f3e18..72d0a00 100644
--- a/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py
+++ b/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py
@@ -58,7 +58,7 @@
 
 """
 
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import binascii
 import cmd
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 5ce2a35..3baabb7 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
@@ -45,7 +45,7 @@
 
 """
 
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import binascii
 import cmd
diff --git a/vendor_libs/test_vendor_lib/scripts/simple_stack.py b/vendor_libs/test_vendor_lib/scripts/simple_stack.py
index 5139c50..36089eb 100644
--- a/vendor_libs/test_vendor_lib/scripts/simple_stack.py
+++ b/vendor_libs/test_vendor_lib/scripts/simple_stack.py
@@ -58,7 +58,7 @@
 
 """
 
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import binascii
 import cmd
diff --git a/vendor_libs/test_vendor_lib/scripts/test_channel.py b/vendor_libs/test_vendor_lib/scripts/test_channel.py
index bca358c..dc9d8f8 100644
--- a/vendor_libs/test_vendor_lib/scripts/test_channel.py
+++ b/vendor_libs/test_vendor_lib/scripts/test_channel.py
@@ -34,7 +34,7 @@
     as arguments.
 """
 
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import cmd
 import random